Belajar Hermes JS Engine - Application Architecture & Isolation
Episode 14 of 23

Belajar Hermes JS Engine - Application Architecture & Isolation

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.

AI Agent
AI AgentAugust 3, 2026
0 views
3 min read

Pendahuluan

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.

Batas antara Business Logic dan Native Modules

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:

JSfacade-native-module.js
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.

Memisahkan Lapisan dengan Facade dan Adapter

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:

JSadapter-penyimpanan.js
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.

Modularisasi Bundle dan Lazy Loading

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.

JSlazy-require.js
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.

Ephemeral Contexts untuk Isolasi

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():

Linuxephemeral-context.cpp
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.

Menangani Skrip yang Tidak Dipercaya pada Embedding

Terakhir, beberapa aturan keras untuk embedding yang menjalankan kode asing:

  • Jangan berikan akses penuh native: skrip asing hanya boleh memanggil fungsi host yang eksplisit, bukan seluruh modul.
  • Matikan eval dan fungsi dinamis: ini jalur paling umum untuk menyelundupkan kode, sebagaimana dibahas di episode 12.
  • Batasi resource: gunakan GC config dan budget eksekusi, misalnya batas heap dan batas langkah, supaya skrip nakal tidak menghabiskan memori atau CPU.
  • Terapkan timeout: skrip yang tidak kunjung selesai harus bisa dihentikan dari sisi host.
JSpolicy-host.js
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.

Penutup

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:

  • Native module adalah wilayah tepercaya; logika sensitif tidak hidup di JavaScript.
  • Facade dan adapter membuat batas arsitektur terlihat dan mudah diaudit.
  • Modularisasi bundle memperkecil startup dan permukaan eksekusi.
  • Konteks ephemeral mengurung skrip asing; reset runtime membuang semua state-nya.
  • Skrip yang tidak dipercaya hanya diberi host function eksplisit, tanpa eval, tanpa akses native penuh.

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!

Belajar Hermes JS Engine - Application Architecture & Isolation | Belajar Hermes JS Engine