NX8V — Server & Web
Panduan lengkap membangun aplikasi web/server dengan NX8V.
Untuk aplikasi desktop (jendela WebView2, tray, dialog), lihat desktop.md. Untuk gambaran umum runtime, lihat README.md.
Daftar isi:
- Alur kerja
- Membuat proyek
- Struktur proyek
- HTTP server
- Router
- Static file server
- WebSocket server
- HTTP/HTTPS client + fetch
- Modul pendukung
- CLI server
- Deployment
- Contoh di repo
- Uji tiga server
- Catatan 24 Oktober 2026
1. Alur kerja
nx8v init # buat proyek (project.json + server.js + public/)
nx8v dev # development: simpan HTML memuat ulang browser
nx8v start # production: jalankan dari project.json
nx8v logs # lihat nx8v.log
nx8v stop # hentikan server
nx8v package # folder distribusi siap kirim
2. Membuat proyek
cd D:\proyek-saya
nx8v init
name [proyek-saya]: aplikasi-toko
description [An NX8V application]: Aplikasi toko sederhana
port [3000]: 4000
Hasil:
D:\proyek-saya\
├── project.json # "dependencies": {}
├── server.js
├── modules\ # folder modul, masih kosong
└── public\
├── index.html
├── about.html
└── assets\
├── css\style.css
├── js\app.js
├── fonts\
└── images\32x32.png # ikon merek NX8V
Folder assets tetap ada. CSS, JS, font, dan gambar tinggal di dalamnya. Di HTML pathnya ditulis pendek: /css/style.css, /js/app.js, dan /images/32x32.png. NX8V mencari berkas itu di public/ dulu, lalu di public/assets/, sebelum server.js dijalankan.
Halaman juga memasang <link rel="icon" href="/images/32x32.png">. Permintaan otomatis /favicon.ico memakai ikon yang sama bila proyek belum punya favicon.ico sendiri.
project.json:
{
"name": "aplikasi-toko",
"version": "1.0.0",
"description": "Aplikasi toko sederhana",
"main": "server.js",
"nx8v": {
"port": 4000,
"desktop": {
"icon": "./images/32x32.png",
"state": "normal",
"width": 800,
"height": 560,
"minWidth": 640,
"minHeight": 400,
"center": true,
"maximizable": true,
"resizable": true,
"titlebar": true,
"devtools": false,
"singleInstance": true,
"productName": "aplikasi-toko",
"shortcutName": "aplikasi-toko",
"artifactName": "aplikasi-toko-Setup-${version}.exe"
}
},
"dependencies": {},
"repository": {
"type": "git",
"url": "https://github.com/nx8v/dependencies.git"
},
"scripts": {
"start": "nx8v start",
"dev": "nx8v dev",
"stop": "nx8v stop"
}
}
"icon": "./images/32x32.png" menunjuk berkas public/assets/images/32x32.png. Path panjang seperti ./public/assets/images/32x32.png tetap berlaku bila berkasnya ada di situ.
Port bisa dioverride tanpa mengubah file: nx8v start --port=5000, atau lewat environment NX8V_PORT / PORT (dibaca server.js).
nx8v init tidak menulis objek website dan tidak menaruh <nx8v-spa> di index.html. SPA, PWA, judul untuk tautan yang dibagikan, serta req.query, req.cookies, req.fields, dan req.files ada di runtime. Proyek baru memakainya setelah project.json dan HTML diubah seperti di bagian SPA dan PWA. server.js hasil init hanya mencatat permintaan (kecuali /.well-known/) dan menjawab /api/hello. Modul yang namanya ada di dependencies dan punya server.cjs menjawab lebih dulu, tanpa baris tambahan di server.js.
3. Struktur proyek
proyek/
├── project.json # metadata, port, desktop, dependencies
├── server.js # entry point (main)
├── modules/ # modul proyek; nama dan index.js wajib
│ └── contoh/
│ └── index.js
├── public/
│ ├── index.html
│ ├── about.html
│ └── assets/
│ ├── css/
│ ├── js/
│ ├── fonts/
│ └── images/
└── nx8v.log # dibuat otomatis saat `nx8v start`
NX8V melayani berkas di public/ sebelum server.js. Menambah halaman cukup dengan menaruh file di folder itu:
| URL | File yang dicari |
|---|---|
/ | public/index.html |
/about | public/about.html lalu public/about/index.html |
/about/data/u | public/about/data/u.html, about/data.html, about.html |
/js/dll.js | public/assets/js/dll.js. Path halaman diawali /js |
/css/style.css | public/assets/css/style.css |
/images/32x32.png | public/assets/images/32x32.png |
/favicon.ico | public/favicon.ico, lalu ikon public/assets/images/32x32.png |
/contoh | modules/contoh/index.js |
Bila tidak ada berkas yang cocok, NX8V menanyakan modules/<nama>/server.cjs untuk setiap nama di dependencies. handle(req, res) yang mengembalikan true memiliki permintaan itu. Sisanya masuk ke server.js, misalnya GET /api/hello. server.js hasil init tetap pendek dan tidak memuat pemetaan folder.
SPA
Dua syarat sekaligus. project.json memuat objek website dengan "SPA": true, dan public/index.html memuat tag <nx8v-spa>. Tag itu boleh diletakkan di mana saja di halaman index. Bila keduanya ada, klik tautan tetap di dokumen index dan isi tautan masuk ke dalam tag. Bila salah satu tidak ada, URL kembali ke berkas HTML-nya sendiri. Objek website yang dipakai adalah yang sejajar dengan name di project.json.
"website": {
"SPA": true,
"PWA": false
}
Klik tautan pada origin yang sama mengisi slot dan mengganti URL. Yang dilewati: /api, berkas berekstensi, target selain _self, dan tautan unduhan. Formulir GET dan POST dengan syarat yang sama menaruh hasil di dalam tag, dan URL mengikuti action.
Halaman tujuan boleh membungkus potongan yang masuk slot dengan <nx8v-page>. Tanpa tag itu, isi <body> yang dipakai, termasuk <script> halaman itu, dan skrip tersebut dijalankan. Berkas yang sendiri berisi <nx8v-spa> tanpa <nx8v-page> tidak disalin ke dalam slot. Berkas tanpa <html>, misalnya teks polos, masuk utuh.
Nama halaman yang sama dengan nama modul, misalnya /form dan modul form, membuka public/form.html saat browser meminta HTML. import dari /form tetap menerima modules/form/index.js.
<nx8v-page>
<h1>About</h1>
<p>Isi yang masuk ke slot.</p>
</nx8v-page>
Membuka URL langsung, memuat ulang, atau melihat sumber halaman memakai cangkang index.html. <title> dan <meta property="og:title"> diambil dari halaman tujuan. Bila halaman tidak punya judul, segmen pertama dipakai, jadi help.html yang hanya berisi <h1> tetap berjudul Help. Deskripsi diambil dari <meta name="description">, atau dari paragraf pertama. og:description, og:image (dari meta atau <img> pertama), og:url, dan <link rel="canonical"> ikut ditulis. Isi <nx8v-page> sudah berada di dalam <nx8v-spa>. Tautan yang dibagikan membawa judul, deskripsi, dan isi halaman itu. Judul index disimpan di data-shell-title. Jalur penuh ada di data-path, ekor URL di data-rest. Kembali ke / mengosongkan slot dan memulihkan judul cangkang.
Klik menunggu respons sebelum URL diganti. Bila permintaan gagal dan tidak ada salinan offline, slot menampilkan Gagal memuat halaman. Hash ikut dibawa dan menggulir ke elemen dengan id itu. Halaman baru mulai dari atas. Tombol kembali memulihkan posisi gulir. Tautan yang path-nya sama dengan URL saat ini mendapat aria-current="page". Setiap <nx8v-spa> diisi. Setelah isi tiba, halaman mengirim peristiwa nx8v-route dengan detail.path, detail.rest, dan detail.hash. Salinan isi slot disimpan di cache nx8v-spa, jadi klik menu yang pernah dibuka tetap tampil saat jaringan putus.
Skrip di halaman meminta berkas asli dengan header X-Nx8v-Fragment: 1. Respons itu adalah about.html, bukan cangkang. Header X-Nx8v-Data berisi ekor URL, misalnya asasas untuk /about/asasas.
Selama permintaan masih berjalan, slot dapat menampilkan spinner dari modul spinner bila nama itu tertulis di dependencies. Halaman yang selesai sebelum modul siap langsung menampilkan isi, tanpa jeda tambahan. Atribut pada tag:
| Atribut | Nilai | Bila tidak ditulis |
|---|---|---|
size | small (20px), medium (40px), large (60px) | medium |
color | warna CSS | #007bff |
message | teks di samping spinner | Memuat... |
position | center, top, bottom | center |
type | inline, overlay, button | inline |
message="" meniadakan teks. type="overlay" menutup layar. Warna type="button" di modul tetap putih.
<nx8v-spa size="small" color="#EA580C" message="" position="center" type="inline"></nx8v-spa>
Log untuk /about/asasas:
route: /about/asasas -> index.html | spa | about.html | data: asasas
Ekor URL tercatat sebagai data. Berkas yang dilayani tetap about.html.
PWA
"PWA": true di objek website yang sama membuat aplikasi bisa dipasang dari browser. Halaman HTML mendapat manifes, warna tema, dan pendaftaran service worker.
"website": {
"SPA": true,
"PWA": true
}
| URL | Isi |
|---|---|
/manifest.webmanifest | nama dan deskripsi dari project.json, start_url /, display standalone |
/nx8v-sw.js | service worker |
/nx8v-icon-192.png | ikon 192, diperbesar dari public/assets/images/32x32.png |
/nx8v-icon-512.png | ikon 512 dari berkas yang sama |
"PWA": true memasang manifes, ikon, dan service worker pada nx8v dev maupun nx8v start. Warna, tampilan, dan URL awal dibaca dari objek website yang sama. Bila tidak diisi, tema #3b82f6, latar #ffffff, display standalone, dan start_url /.
"website": {
"SPA": true,
"PWA": true,
"theme_color": "#EA580C",
"background_color": "#ffffff",
"display": "standalone",
"start_url": "/"
}
Nama cache mengikuti version di project.json, misalnya nx8v-pwa-1.0.0. Saat dipasang, worker menyimpan /, manifes, dan kedua ikon. Halaman juga mendapat <link rel="apple-touch-icon">. Pemasangan memakai ikon di bilah alamat browser.
Service worker mengambil jaringan lebih dulu dengan cache: 'no-store', lalu menyimpan salinan di cache-nya sendiri. Saat jaringan putus, halaman dan berkas yang pernah dibuka dilayani dari simpanan itu. Permintaan /api, pemeriksa nx8v dev, dan isi slot SPA tidak melewati service worker, jadi klik antar menu tetap bisa dibatalkan. Isi slot yang sudah pernah tampil dibaca lagi dari cache nx8v-spa.
Bila PWA mati, keempat URL itu tidak dibuat. Halaman melepas service worker yang sudah terpasang.
Modul
Modul proyek ditaruh di modules/. Dua hal wajib:
- Nama di
dependenciespadaproject.json. - Berkas masuk
index.jsdi folder yang ditunjuk nama itu.
"dependencies": {
"contoh": "1.0.0",
"photos": "1.0.0"
}
Kuncinya nama modul. Nilainya versi. Folder selalu modules/ ditambah nama itu, jadi "contoh": "1.0.0" berarti modules/contoh/index.js.
| Yang dibuat | Bentuk |
|---|---|
| Nama | kunci contoh |
| Versi | 1.0.0 |
| Folder | modules/contoh |
| Berkas masuk | modules/contoh/index.js |
Di HTML cukup nama itu:
<script type="module" src="contoh"></script>
Browser meminta /contoh. NX8V mengembalikan modules/contoh/index.js. /contoh/index.js adalah berkas yang sama. Nama yang tidak tertulis di dependencies tidak dilayani.
require('./modules/photos') di server.js juga membuka index.js folder itu. photos untuk server. contoh untuk halaman. nx8v init membuat folder modules/, "dependencies": {}, dan repository yang menunjuk ke mega-repo internal NX8V.
Modul yang juga melayani HTTP menaruh server.cjs di folder yang sama:
function handle(req, res) {
const clean = req.url.split("?")[0];
if (req.method !== "POST" || clean !== "/api/contoh") return false;
res.writeHead(200, { "Content-Type": "application/json" });
res.end(JSON.stringify({ ok: true }));
return true;
}
module.exports = { handle };
NX8V memuat berkas itu saat permintaan pertama, hanya bila nama foldernya tertulis di dependencies dan berkasnya ada. handle menerima req dan res yang sama dengan listener server.js. Balasan true berarti modul itu yang menjawab, dan listener server.js tidak dipanggil. Balasan false meneruskan permintaan ke modul berikutnya, lalu ke server.js. Body tetap sampai lewat req.on('data') dan req.on('end').
server.js tidak perlu require modul itu. Menambah atau menghapus nama di dependencies sudah cukup. Setelah server.cjs atau project.json berubah, jalankan ulang nx8v.
Dependensi
repository di project.json adalah tempat modul diambil dan diterbitkan. Bawaannya repo internal NX8V. Saat materi dependensi dipublikasikan dari repo milik user, url diganti.
"repository": {
"type": "git",
"url": "https://github.com/nx8v/dependencies.git"
}
Satu versi sama dengan satu GitHub Release. Nama dan versi pada perintah menjadi tag <nama>-<versi>. Aset rilis disalin ke modules/<nama>/. Berkas masuknya index.js. Berkas lain di folder yang sama, misalnya app.js, ikut diunduh dan ikut diterbitkan.
Perintah dijalankan di folder proyek:
| Perintah | Fungsi |
|---|---|
nx8v i contoh@1.0.2 | pasang. Tag yang dicari: contoh-1.0.2 |
nx8v u contoh@1.0.2 | ganti versi modul yang sudah ada di dependencies. Angka versinya yang diganti |
nx8v p contohnamalain@1.0.0 | terbitkan isi modules/contohnamalain/ ke repository |
nx8v d contoh | hapus contoh dari dependencies, nx8v.lock, dan modules/contoh |
nx8v r contoh | hapus rilis, tag, dan folder contoh dari repository |
Pasang menulis tiga tempat:
"dependencies": {
"contoh": "1.0.2"
}
modules/contoh/index.js
{
"dependencies": {
"contoh": {
"version": "1.0.2",
"resolved": "https://github.com/nx8v/dependencies/releases/tag/contoh-1.0.2"
}
}
}
resolved di nx8v.lock adalah halaman rilis yang dipakai saat pemasangan. Nama yang tidak ada tag-nya tidak ditulis ke dependencies maupun modules. Bila tag tidak ketemu, perintah menampilkan daftar rilis yang ada.
Perbarui hanya untuk nama yang sudah terpasang. nx8v u contoh@1.0.2 mengganti berkas di modules/contoh/, versi di dependencies, dan catatan di nx8v.lock. Nama yang belum ada ditolak; pasang lebih dulu dengan nx8v i.
Terbitkan membaca modules/contohnamalain/. Folder itu wajib berisi index.js. Setiap berkas di folder itu dilampirkan sebagai aset rilis contohnamalain-1.0.0. Perintah ini memakai GitHub CLI dan akun yang sudah masuk:
gh auth login
nx8v p contohnamalain@1.0.0
Setelah terbit, dependencies dan nx8v.lock di proyek itu ikut mencatat versi tersebut. Rilis yang tag-nya sudah ada tidak ditimpa.
Hapus mengeluarkan nama dari daftar proyek. nx8v d contoh menghapus kunci contoh di project.json dan nx8v.lock, lalu menghapus folder modules/contoh.
Hapus dari repository dipakai pemilik repo. nx8v r contoh menghapus setiap rilis bertag contoh-<versi>, tag itu, dan folder contoh di cabang utama. Perintah ini memakai GitHub CLI yang sudah masuk, sama seperti nx8v p. Daftar di proyek yang sedang dibuka tidak diubah.
4. HTTP server
const http = require('http');
const server = http.createServer((req, res) => {
console.log(req.method, req.url);
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ ok: true }));
});
server.listen(3000, () => console.log('http://localhost:3000'));
HTTPS memakai modul https. Berkas PFX dibaca sebagai Buffer (tanpa encoding). Kunci PEM dibaca sebagai teks.
const https = require('https');
const fs = require('fs');
https.createServer({
pfx: fs.readFileSync('server.pfx'),
passphrase: 'rahasia'
}, (req, res) => {
res.end(req.httpVersion);
}).listen(8443);
https.createServer({
cert: fs.readFileSync('server.crt', 'utf8'),
key: fs.readFileSync('server.key', 'utf8')
}, (req, res) => {
res.end(req.httpVersion);
}).listen(8444);
Sertifikat PFX berunding TLS 1.2 dan 1.3. Pasangan cert dan key menerima BEGIN CERTIFICATE bersama BEGIN PRIVATE KEY (PKCS#8) atau BEGIN RSA PRIVATE KEY. Di Windows, PEM berunding TLS 1.2. Di Linux, PEM berunding TLS 1.2 dan 1.3. Kunci bertipe BEGIN ENCRYPTED PRIVATE KEY belum didukung; bungkus kunci itu ke dalam PFX. ALPN menawarkan h2 lalu http/1.1. req.httpVersion berisi "2.0" saat klien memakai HTTP/2, dan "HTTP/1.1" saat klien memakai HTTP/1.1. HTTP/2 tanpa TLS (preface PRI * HTTP/2.0) juga diterima oleh http.createServer. Build Linux menautkan OpenSSL lewat NX8V_USE_OPENSSL, untuk klien https:// dan https.createServer. Rilis v0.2.2 memuat server TLS ini. Ketiga panel di port 6002 menjalankan biner yang sama.
Request (req):
| Properti/Method | Keterangan |
|---|---|
req.method | GET, POST, … |
req.url | path + query, apa adanya |
req.httpVersion | "HTTP/1.1" pada HTTP/1.1, "2.0" pada HTTP/2 |
req.headers | objek header (nama asli dari klien) |
req.query | objek query string, ?a=1 menjadi { a: "1" } |
req.cookies | objek dari header Cookie |
req.fields | bidang formulir application/x-www-form-urlencoded atau multipart/form-data |
req.files | berkas unggahan. Setiap nama berisi { filename, type, data } dan data adalah Buffer |
req.on('data', fn) | body (bila ada) |
req.on('end', fn) | body selesai |
Response (res):
| Method | Keterangan |
|---|---|
res.writeHead(status[, message][, headers]) | kirim status + header. Tanpa message, kalimat status mengikuti kode: 404 menjadi Not Found |
res.setHeader(name, value) | set satu header |
res.cookie(name, value[, opsi]) | tambah Set-Cookie. Opsi: path, maxAge, httpOnly, secure, sameSite |
res.getHeader(name) | baca header |
res.write(chunk) | tulis body (di-buffer). chunk boleh string atau Buffer |
res.end([chunk][, cb]) | kirim respons, tutup koneksi. Byte 0x00 di dalam PNG ikut terkirim |
res.statusCode | status default 200. Mengubah angka ini juga mengganti kalimat status |
fs.readFile dan fs.readFileSync tanpa encoding mengembalikan Buffer, jadi gambar tidak rusak saat dikirim lewat res.end. Encoding 'utf8' tetap mengembalikan string. data.toString() memakai teks UTF-8.
Event server: listening, request, connection, close, error, websocket.
Catatan: server membalas Connection: close (satu permintaan per koneksi). Pipelining belum didukung. Body di atas 8 MB dibalas 413 Payload Too Large.
HTML, termasuk cangkang SPA, dikirim dengan Cache-Control: no-store dan tetap bisa di-gzip. JavaScript, CSS, dan berkas modul tanpa ekstensi dikirim dengan ETag dan Cache-Control: no-cache, jadi browser menanyakan ulang ke server. Gambar, font, dan berkas lain dikirim dengan ETag, Cache-Control: public, max-age=3600, Range (206), dan gzip bila klien meminta Accept-Encoding: gzip. If-None-Match yang cocok pada kelompok itu membalas 304.
5. Router
const app = require('router')();
app.use((req, res) => {
console.log(req.method, req.url); // middleware global
});
app.get('/user/:id', (req, res) => {
res.json({ id: req.params.id }); // param dinamis
});
app.post('/api/login', (req, res) => {
res.status(201).json({ ok: true, token: 'abc123' });
});
app.get('/lama', (req, res) => res.redirect('/baru'));
app.notFound((req, res) => {
res.status(404).json({ error: 'tidak ada rute', path: req.url });
});
app.listen(5400, () => console.log('http://localhost:5400'));
| API | Keterangan | |
|---|---|---|
app.get/post/put/delete/patch/all(path, handler) | daftar rute | |
app.use([path,] fn) | middleware (global atau ber-scope) | |
app.notFound(fn) | handler 404 kustom | |
app.listen(port[, cb]) | jalankan server, mengembalikan http.Server | |
req.params | hasil parse param rute | |
res.status(code) | set status (chainable) | |
res.json(obj) | kirim JSON + header otomatis | |
| `res.send(str\ | Buffer)` | kirim teks/biner |
res.redirect(url[, code]) | redirect (default 302) |
Pola path yang didukung:
/user/:id → req.params.id
/file/:name? → opsional
/static/* → req.params[0] berisi sisa path
Middleware bisa menghentikan rantai dengan return false.
6. Static file server
Versi produksi dari static handler (ETag, Range, HEAD, cache):
const http = require('http');
const serve = require('static');
const handler = serve('public', { maxAge: 3600, index: 'index.html' });
http.createServer((req, res) => handler(req, res)).listen(3000);
// atau: app.use(handler) (di router)
| Opsi | Default | Keterangan |
|---|---|---|
maxAge | 0 | detik untuk Cache-Control: public, max-age=N |
index | index.html | file untuk direktori |
etag | true | kirim ETag + dukung If-None-Match (304) |
ranges | true | dukung Range (206 Partial Content) |
Perilaku:
GET/HEADsaja; method lain → 405 + headerAllowIf-None-Match/If-Modified-Sincecocok → 304 (tanpa body)Range: bytes=0-99→ 206 +Content-Range- Range tidak valid → 416
..pada path (termasuk%2e%2e) → 403- File tidak ada → 404
7. WebSocket server
const http = require('http');
const server = http.createServer((req, res) => res.end('ws server'));
server.on('websocket', (ws) => {
console.log('client tersambung');
ws.send('selamat datang');
ws.on('message', (msg) => {
console.log('terima:', msg);
ws.send('echo: ' + msg);
});
ws.on('close', () => console.log('client putus'));
});
server.listen(5300);
Upgrade ditangani otomatis: klien yang mengirim header Upgrade: websocket ke port yang sama akan diubah menjadi koneksi WebSocket (RFC 6455).
| API | Keterangan | |
|---|---|---|
server.on('websocket', fn) | koneksi WebSocket baru | |
| `ws.send(text \ | Buffer)` | kirim frame |
ws.on('message', fn) | pesan masuk (string) | |
ws.on('close', fn) | koneksi ditutup | |
ws.close() | tutup dari server |
Ping/pong dijawab otomatis. Klien (browser) bisa memakai API WebSocket biasa:
const ws = new WebSocket('ws://localhost:5300/');
ws.onmessage = (e) => console.log(e.data);
8. HTTP/HTTPS client + fetch
http.get / http.request
const http = require('http');
http.get('http://127.0.0.1:3000/api/hello', (res) => {
console.log('status:', res.statusCode, res.statusMessage);
let body = '';
res.on('data', (c) => { body += c; });
res.on('end', () => console.log(body));
});
const https = require('https'); // sama, untuk https://
const req = https.request({
hostname: 'jsonplaceholder.typicode.com',
path: '/photos?_limit=5',
method: 'GET',
headers: { 'Accept': 'application/json' },
}, (res) => {
let body = '';
res.on('data', (c) => { body += c; });
res.on('end', () => console.log(JSON.parse(body).length, 'item'));
});
fetch (global)
fetch('https://jsonplaceholder.typicode.com/photos?_limit=5')
.then((r) => {
if (!r.ok) throw new Error('HTTP ' + r.status);
return r.json();
})
.then((photos) => console.log(photos.length, 'foto'))
.catch((e) => console.error(e.message));
| Objek respons | Keterangan |
|---|---|
res.ok | true bila status 200–299 |
res.status | kode status |
res.statusText | teks status |
res.headers | objek header (lowercase) |
res.text() | Promise<string> |
res.json() | Promise<object> |
Catatan HTTPS: di Windows memakai WinHTTP (TLS 1.2/1.3, sertifikat dari cert store Windows, redirect dan de-chunk otomatis). Di Linux aktif bila dikompilasi dengan -DNX8V_USE_OPENSSL.
Chunked transfer (Transfer-Encoding: chunked) ditangani otomatis pada respons HTTP biasa.
9. Modul pendukung
Modul yang paling sering dipakai server:
| Modul | Kegunaan |
|---|---|
fs | baca/tulis file. readFile tanpa encoding mengembalikan Buffer |
path | join, resolve, dirname, basename, extname, parse, format |
querystring | parse, stringify, escape, unescape, URLSearchParams |
url | parse URL (href, pathname, search, host, …) |
zlib | gzipSync / gunzipSync / deflateSync / inflateSync |
crypto | createHash (md5/sha1/sha256), randomBytes, randomUUID |
Buffer | data biner (index access, toString('hex'/'base64'), slice, concat) |
stream | Readable/Writable/PassThrough + pipe |
events | EventEmitter |
net | TCP mentah (createServer, connect) |
dns | lookup, resolve, resolve4 |
child_process | exec, spawn |
os | platform, cpus, networkInterfaces, homedir, … |
db | open — SQLite, PostgreSQL, dan MySQL |
require('db')
Pemanggilan memblokir sampai database menjawab. Kegagalan melempar Error. Placeholder di SQL adalah ?. Pada PostgreSQL tanda itu dikirim sebagai $1, $2, dan seterusnya.
const db = require('db');
const sql = db.open({ driver: 'sqlite', file: 'data.db' });
sql.exec('CREATE TABLE IF NOT EXISTS t (id INTEGER PRIMARY KEY, nama TEXT)');
const tulis = sql.run('INSERT INTO t (nama) VALUES (?)', ['Budi']);
// tulis.changes, tulis.lastInsertRowid
const satu = sql.get('SELECT * FROM t WHERE nama = ?', ['Budi']); // objek, atau null
const semua = sql.all('SELECT * FROM t'); // array
sql.close();
driver | Bawaan |
|---|---|
sqlite | file = data.db. :memory: membuka basis di memori. timeout milidetik, bawaan 10000 |
postgres atau postgresql | host 127.0.0.1, port 5432, user postgres. password boleh ditulis pass. database kosong memakai nama user |
mysql atau mariadb | host 127.0.0.1, port 3306, user root. database kosong memakai nama user |
| Metode | Hasil |
|---|---|
exec(sql[, params]) | menjalankan SQL, tanpa nilai kembali |
run(sql[, params]) | { changes, lastInsertRowid } |
get(sql[, params]) | satu objek, atau null |
all(sql[, params]) | array objek |
close() | menutup koneksi |
PostgreSQL menerima sandi cleartext, md5, dan SCRAM-SHA-256. MySQL menerima mysql_native_password dan jalur cepat caching_sha2_password. Pertukaran autentikasi penuh caching_sha2 (penanda 0x01 0x04) belum dikerjakan dan mengembalikan pesan kesalahan. SQLite menyalakan foreign_keys dan tidak memuat ekstensi.
Contoh querystring:
const qs = require('querystring');
qs.parse('a=1&b=dua&c=x%20y'); // { a: '1', b: 'dua', c: 'x y' }
qs.stringify({ x: 1, y: 'a b' }); // 'x=1&y=a+b'
10. CLI server
| Perintah | Fungsi |
|---|---|
nx8v init | wizard pembuatan proyek |
nx8v i <nama>@<versi> | pasang modul dari repository di project.json |
nx8v u <nama>@<versi> | ganti versi modul yang sudah terpasang |
nx8v p <nama>@<versi> | terbitkan modules/<nama> ke repository |
nx8v d <nama> | hapus modul dari dependencies |
nx8v r <nama> | hapus rilis, tag, dan folder modul dari repository |
nx8v dev [--port=N] | development: simpan HTML memuat ulang browser; simpan JS me-restart server |
nx8v start [--port=N] | jalankan dari project.json |
nx8v stop [--port=N] | hentikan server proyek |
nx8v list | daftar server berjalan (PID, port, proyek, direktori) |
nx8v logs [--lines=N] | ekor nx8v.log proyek ini |
nx8v test [path] | jalankan *.test.js |
nx8v package | folder distribusi (dist/<nama>/) |
nx8v dev
Memantau folder proyek, termasuk file di root seperti server.js. Dilewati: node_modules, .git, build, dist, .nx8v.
Simpan .html, .css, atau .svg memuat ulang halaman yang sedang terbuka. Server tetap jalan:
[watch] change detected: D:\proyek\public\index.html
[dev] browser reload
Simpan .js, .mjs, atau .json me-restart server, lalu halaman yang sama ikut memuat ulang setelah server hidup lagi:
[watch] change detected: D:\proyek\server.js
[dev] stopping server for restart...
[dev] restarting D:\proyek\server.js
[dev] server started (pid 1250)
Setelah nx8v diperbarui, refresh halaman sekali supaya skrip pemantau masuk. Simpan berikutnya memuat ulang sendiri. Bila server crash karena error sintaks, supervisor tetap hidup — perbaiki file dan server otomatis menyala lagi.
Komponen HTML (partial)
Tag apa pun bisa memuat file HTML lain. nx8v menggabungkannya saat halaman dikirim, tanpa JavaScript di browser:
<header partial="./header.html"></header>
./header.html dibaca dari folder yang sama dengan halaman (public/). Isi file masuk ke dalam tag, dan atribut partial dihapus. Simpan file komponen saat nx8v dev berjalan: halaman yang terbuka memuat ulang sendiri.
Path di luar folder public ditolak.
nx8v list
PID PORT PROJECT DIR
1360 4000 Aplikasi demo D:\NX8V\example
13204 5202 toko-demo D:\NX8V\build\demotest
nx8v test
test('penjumlahan', () => {
assert.equal(1 + 1, 2);
assert.deepEqual([1, 2], [1, 2]);
assert.throws(() => { throw new Error('x'); });
});
Output ✓/✗ per test + ringkasan; exit code 1 bila ada kegagalan. Assertion: equal, strictEqual, notEqual, deepEqual, deepStrictEqual, ok, truthy, notOk, throws, doesNotThrow.
11. Deployment
Windows
# jalankan di background lalu cek
nx8v start
nx8v list
curl http://127.0.0.1:4000/
# hentikan
nx8v stop
Linux (systemd)
sudo ./install.sh --service /path/to/project
Menghasilkan unit dari packaging/nx8v.service:
[Unit]
Description=NX8V application
After=network.target
[Service]
Type=simple
WorkingDirectory=@PROJECT@
ExecStart=/usr/bin/env nx8v start
Restart=on-failure
RestartSec=2
Distribusi
nx8v package # dist/<nama>/ berisi exe + proyek + start.cmd
nx8v package --name=toko --out=release
Folder hasil bisa di-zip dan dikirim; penerima cukup menjalankan start.cmd.
12. Contoh di repo
| File | Isi |
|---|---|
samples/httpserver.js | HTTP server + JSON endpoint |
samples/router.js | router: param, middleware, redirect, notFound |
samples/static_server.js | static handler (ETag/Range) |
samples/websocket.js | WebSocket server (echo) |
samples/wsclient.js | WebSocket client |
samples/fs.js | operasi file |
samples/timers.js | setTimeout/setInterval |
samples/basic.test.js | contoh test |
example/ | proyek lengkap: galeri foto dari API (fetch HTTPS) |
13. Uji tiga server
Uji 23 Oktober 2026 di tiga server Docker pada WSL Ubuntu-22.04. Susunan server ada di D:\server\README.md. NX8V berjalan sebagai proses sendiri. aaPanel, CyberPanel, dan Apache hanya meneruskan jalur /nx8v/. Situs yang sudah ada tetap di akarnya.
Server kedua adalah CyberPanel penuh versi 1.9, container cyberpanel, image cyberpanel-ssh:latest, CentOS 7.6. Narasinya ada di D:\server\CyberPanel\README.md. Panel itu memakai stack hosting umum: lscpd (panel, port 8090), OpenLiteSpeed (web :80/:443 dan admin :7080), MariaDB, Postfix/Dovecot, PowerDNS, dan Pure-FTPd. OpenLiteSpeed di sini adalah web server di dalam CyberPanel. Layanan di container ini dinyalakan entrypoint docker/cyberpanel/entrypoint.sh, karena systemd di dalam container WSL tidak lengkap.
Akses dari Windows memakai IP WSL. Di mesin uji, localhost tidak meneruskan port container. IP saat uji: 192.168.144.198. Cek ulang dengan wsl -d Ubuntu-22.04 -u root hostname -I.
| Server | URL | Halaman | /nx8v/api/where |
|---|---|---|---|
| aaPanel | http://192.168.144.198:8080/nx8v/ | HTTP 200 | {"runtime":"nx8v","host":"437e50fe88dd"} |
| CyberPanel | http://192.168.144.198:8082/nx8v/ | HTTP 200 | {"runtime":"nx8v","host":"DANTRIK"} |
| Apache | http://192.168.144.198:8083/nx8v/ | HTTP 200 | {"runtime":"nx8v","host":"acaf18fee8e1"} |
Halaman menampilkan judul NX8V Server & web, lalu mengisi nama host dari fetch('api/where'). Situs NxPHP di CyberPanel (Host: nxd-cyber.local pada port 8082) tetap HTTP 200 setelah jalur /nx8v/ dipasang.
Proyek uji: D:\server\nx8v-app (project.json, server.js, public/index.html). Port dengar 6002. Binary Linux yang dipakai build/linux/nx8v (glibc 2.34).
| Server | Tempat proses | Proxy |
|---|---|---|
aaPanel, container aapanel (glibc 2.35) | /opt/nx8v, 127.0.0.1:6002 | D:\server\docker\aapanel\nx8v-proxy.conf |
Apache, container apache (glibc 2.41) | /opt/nx8v, 127.0.0.1:6002 | D:\server\docker\apache\nx8v-proxy.conf |
CyberPanel 1.9, container cyberpanel, CentOS 7.6 (glibc 2.17) | proses di WSL, gateway 172.18.0.1:6002 | context /nx8v/ pada vhost nxlocal, D:\server\nx8v-app\ols-nx8v.snippet |
CentOS 7.6 di CyberPanel memakai glibc 2.17, jadi binary NX8V (glibc 2.34) dijalankan di WSL. OpenLiteSpeed di dalam CyberPanel meneruskan /nx8v/ ke gateway Docker 172.18.0.1:6002. aaPanel dan Apache menjalankan binary yang sama di dalam masing-masing container.
Menyalakan ulang setelah WSL atau container hidup kembali:
wsl -d Ubuntu-22.04 -u root bash /mnt/d/server/nx8v-app/install-servers.sh
wsl -d Ubuntu-22.04 -u root bash /mnt/d/server/nx8v-app/enable-proxy.sh
14. Catatan 24 Oktober 2026
Yang sudah ada di runtime, dan apakah nx8v init ikut menuliskannya:
| Bagian | Runtime | nx8v init |
|---|---|---|
SPA (website.SPA dan <nx8v-spa>) | ada | tidak |
PWA (website.PWA, manifes, service worker) | ada | tidak |
Judul, deskripsi, dan og:* saat tautan dibagikan | ada, ikut SPA | tidak |
req.query, req.cookies, req.fields, req.files, res.cookie | ada pada setiap permintaan | tidak. Contoh init hanya /api/hello |
| ETag, Range, dan gzip berkas publik selain HTML | ada | tidak perlu di template |
Log /.well-known/ tidak dicetak | ada | ada, satu syarat di server.js |
nx8v --fuzz (korpus + 20.000 mutasi, benih --fuzz=1:20000) | ada | bukan berkas proyek |
crypto.randomBytes / crypto.randomUUID lewat BCryptGenRandom (Windows) dan getrandom (Linux) | ada | bukan berkas proyek |
| Hosting aaPanel, CyberPanel, dan Apache | hosting.md | bukan berkas proyek |
require('db'): SQLite, PostgreSQL, MySQL | ada | bukan berkas proyek |
modules/<nama>/server.cjs untuk nama di dependencies | ada. handle yang mengembalikan true menjawab sebelum server.js | tidak. server.js tetap contoh /api/hello |
JavaScript, CSS, dan modul tanpa ekstensi: Cache-Control: no-cache | ada | tidak perlu di template |
Service worker fetch memakai cache: 'no-store' | ada, bila PWA hidup | tidak |
Parser chunked menolak panjang yang lebih besar dari data yang benar-benar ada, jadi klaim ukuran tidak membuat buffer sebesar angka itu.
Pemasangan lewat SSH, dicatat di hosting.md:
| Server | Hasil |
|---|---|
| aaPanel | nx8v/0.2.2 terpasang di /home/admin/.local/bin/nx8v |
| Apache | nx8v/0.2.2 terpasang di /home/admin/.local/bin/nx8v |
| CyberPanel | berkas terpasang di jalur yang sama, tetapi tidak jalan: libssl.so.3 tidak ada |
Troubleshooting
| Gejala | Penyebab & solusi |
|---|---|
ERR_UNSAFE_PORT di browser | Port diblokir Chrome (mis. 6000). Pakai 3000/4000/8080. |
| Browser reload terus | Sudah diperbaiki; pastikan exe terbaru. |
Cannot find module 'X' | Modul bukan built-in → cek path relatif, atau daftar modul di §9. |
EADDRINUSE / address already in use | Port dipakai proses lain → nx8v list, lalu nx8v stop --port=N. |
| Gambar rusak atau hanya beberapa byte | res.end harus menerima Buffer dari readFile. Pakai exe terbaru. |
404 pada /favicon.ico | Letakkan public/favicon.ico, atau biarkan server memakai public/assets/images/32x32.png. |
invalid URL pada fetch | Skema harus http:// atau https://. |
| Halaman tidak berubah | HTML memakai no-store. JavaScript, CSS, dan modul tanpa ekstensi memakai no-cache. Muat ulang halaman. Perubahan server.cjs butuh nx8v dijalankan ulang. |
Hosting NX8V
Satu aplikasi NX8V. Tiga web server di depannya: aaPanel, CyberPanel, dan Apache.
Situs yang sudah ada tetap di /. Aplikasi NX8V tampil di /nx8v/. Web server hanya meneruskan jalur itu ke proses NX8V. NX8V sendiri yang menulis halaman dan API.
Yang tampil di ketiga server sama.
| Halaman | https://domain/nx8v/ |
| Judul | NX8V Server & web |
| API | https://domain/nx8v/api/where |
| Isi API | {"runtime":"nx8v","host":"nama-mesin"} |
Nama host di halaman diisi dari API itu.
1. Aplikasi
Simpan di /opt/nx8v.
project.json
{
"name": "nx8v-web",
"version": "1.0.0",
"description": "NX8V Server and web",
"main": "server.js",
"nx8v": { "port": 6002 }
}
public/index.html
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="utf-8">
<title>NX8V Server & web</title>
<style>
body { font-family: system-ui, sans-serif; margin: 2rem; }
h1 { color: #1d4ed8; }
</style>
</head>
<body>
<h1>NX8V Server & web</h1>
<p>Halaman ini dilayani oleh runtime NX8V.</p>
<p>Host: <strong id="host">memuat</strong></p>
<script>
fetch('api/where').then(function (r) { return r.json(); }).then(function (j) {
document.getElementById('host').textContent = j.host;
}).catch(function () {
document.getElementById('host').textContent = 'tidak terbaca';
});
</script>
</body>
</html>
server.js mendengar di 127.0.0.1:6002. Awalan /nx8v dibuang sebelum rute dipilih, karena halaman memakai fetch('api/where') dan browser mengirim /nx8v/api/where.
const http = require('http');
const fs = require('fs');
const os = require('os');
const PORT = 6002;
const page = process.cwd().replace(/\/$/, '') + '/public/index.html';
function route(url) {
let p = (url || '/').split('?')[0];
if (p === '/nx8v' || p === '/nx8v/') return '/';
if (p.indexOf('/nx8v/') === 0) return p.slice('/nx8v'.length);
return p;
}
const server = http.createServer((req, res) => {
const clean = route(req.url);
if (clean === '/api/where') {
res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
res.end(JSON.stringify({ runtime: 'nx8v', host: os.hostname() }));
return;
}
if (clean === '/' || clean === '/index.html') {
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
res.end(fs.readFileSync(page, 'utf8'));
return;
}
res.writeHead(404, { 'Content-Type': 'text/plain; charset=utf-8' });
res.end('404');
});
server.listen(PORT, '127.0.0.1', () => {
console.log('nx8v web http://127.0.0.1:' + PORT);
});
Port 6002 hanya untuk mesin itu. Dari internet yang terbuka tetap port web server, 80 atau 443.
2. Dari mana NX8V dipasang
Runtime-nya tidak ikut panel. Di aaPanel, CyberPanel, dan Apache, sumbernya sama: rilis Linux di github.com/iyanrsaleh/nx8v/releases/latest.
Tiap server punya SSH sendiri. Perintah di bawah diketik di terminal SSH server itu, bukan dari mesin lain.
Mesin membutuhkan glibc 2.34 atau lebih baru. install.sh mengunduh nx8v-linux-x64.tar.gz, mencocokkan hash di SHA256SUMS, lalu memasang biner ke ~/.local/bin/nx8v.
curl -fsSL -o install.sh https://raw.githubusercontent.com/iyanrsaleh/nx8v/main/install.sh
sed -i 's/\r$//' install.sh
sh install.sh
sudo install -m 755 "$HOME/.local/bin/nx8v" /usr/local/bin/nx8v
nx8v --version
sh install.sh memasang biner untuk user SSH admin. Hasilnya di ketiga server:
| Server | Hasil |
|---|---|
| aaPanel | nx8v/0.2.2 terpasang di /home/admin/.local/bin/nx8v |
| Apache | nx8v/0.2.2 terpasang di /home/admin/.local/bin/nx8v |
| CyberPanel | biner tidak bisa jalan sendiri (glibc 2.17). Ditambal dengan glibc 2.36 dari Ubuntu 24.04 (patchelf) agar bisa digunakan |
/usr/local/bin membuat nx8v ketemu oleh layanan sistem. Di CyberPanel langkah sudo install tidak dijalankan, karena biner itu tidak bisa dieksekusi di mesin tersebut. Aplikasi di bagian 1 tetap berkas Anda sendiri. Salin project.json, server.js, dan public/index.html ke /opt/nx8v, lalu nyalakan.
cd /opt/nx8v
nx8v start
curl -sS http://127.0.0.1:6002/api/where
Jawaban yang benar:
{"runtime":"nx8v","host":"nama-mesin"}
Agar hidup lagi setelah mesin menyala, pasang unit dari skrip yang sama. Direktori kerja unit itu /opt/nx8v, perintahnya nx8v start.
sudo sh install.sh --service /opt/nx8v
sudo systemctl enable --now nx8v
3. aaPanel
aaPanel di sini memakai Apache. Situs lama tetap di konfigurasi situsnya. Tambahkan penerusan ini pada vhost situs, lalu muat ulang Apache dari panel.
RedirectMatch ^/nx8v$ /nx8v/
<Location /nx8v/>
ProxyPass http://127.0.0.1:6002/
ProxyPassReverse http://127.0.0.1:6002/
</Location>
/nx8v tanpa garis miring diarahkan ke /nx8v/. Modul proxy dan proxy_http harus aktif. httpd -t bersih sebelum Apache dimuat ulang.
Buka https://domain/nx8v/. Judulnya NX8V Server & web. Baris host terisi nama mesin.
4. CyberPanel
Web server CyberPanel adalah OpenLiteSpeed. Container berbasis CentOS 7.6 (glibc 2.17) — terlalu lama untuk biner NX8V (butuh glibc 2.34+, OpenSSL 3). Solusinya: salin glibc 2.36 + OpenSSL 3 dari Ubuntu 24.04, lalu patch biner dengan patchelf agar memakai library baru itu. Tidak ada yang ditimpa di sistem; semuanya di /opt/glibc236.
4a. Pasang library dari Ubuntu 24.04
Jalankan di host Windows (bukan di dalam container):
# Ambil library dari image Ubuntu 24.04 (sekali saja)
docker pull ubuntu:24.04 --quiet
docker create --name tmp-ub24 ubuntu:24.04 true
docker cp tmp-ub24:/lib/x86_64-linux-gnu/libssl.so.3 <dir>\libssl.so.3
docker cp tmp-ub24:/lib/x86_64-linux-gnu/libcrypto.so.3 <dir>\libcrypto.so.3
docker cp tmp-ub24:/lib/x86_64-linux-gnu/libm.so.6 <dir>\libm.so.6
docker cp tmp-ub24:/lib/x86_64-linux-gnu/libgcc_s.so.1 <dir>\libgcc_s.so.1
docker cp tmp-ub24:/lib/x86_64-linux-gnu/libc.so.6 <dir>\libc.so.6
docker cp tmp-ub24:/lib64/ld-linux-x86-64.so.2 <dir>\ld-linux-x86-64.so.2
docker rm tmp-ub24
<dir> adalah direktori sementara, mis. D:\tmp-libs. Semua file berikutnya diletakkan di /opt/glibc236 di dalam container:
# Dari Windows
docker cp <dir>\libssl.so.3 cyberpanel:/opt/glibc236/libssl.so.3
docker cp <dir>\libcrypto.so.3 cyberpanel:/opt/glibc236/libcrypto.so.3
docker cp <dir>\libm.so.6 cyberpanel:/opt/glibc236/libm.so.6
docker cp <dir>\libgcc_s.so.1 cyberpanel:/opt/glibc236/libgcc_s.so.1
docker cp <dir>\libc.so.6 cyberpanel:/opt/glibc236/libc.so.6
docker cp <dir>\ld-linux-x86-64.so.2 cyberpanel:/opt/glibc236/ld-linux-x86-64.so.2
4b. Patch biner nx8v
Pasang patchelf (sekali saja):
curl -fsSL https://github.com/NixOS/patchelf/releases/download/0.18.0/patchelf-0.18.0-x86_64.tar.gz -o /tmp/p.tar.gz
tar -xzf /tmp/p.tar.gz -C /tmp
cp /tmp/patchelf-0.18.0-x86_64/bin/patchelf /usr/local/bin/
Patch interpreter dan RPATH:
NX8V=/home/admin/.local/bin/nx8v
patchelf --set-interpreter /opt/glibc236/ld-linux-x86-64.so.2 $NX8V
patchelf --set-rpath /opt/glibc236 $NX8V
$NX8V --version # nx8v/0.2.2
4c. Jalankan
cd /opt/nx8v
nohup env LD_LIBRARY_PATH=/opt/glibc236 /home/admin/.local/bin/nx8v start >/var/log/nx8v.log 2>&1 &
curl -sS http://127.0.0.1:6002/api/where
Jawaban yang benar:
{"runtime":"nx8v","host":"nama-mesin"}
Auto-start di /opt/entrypoint-cyberpanel.sh (host: docker/cyberpanel/entrypoint.sh):
NX8V=/home/admin/.local/bin/nx8v
if [ -x "$NX8V" ] && [ -f /opt/nx8v/project.json ] && ! pgrep -f "$NX8V start" >/dev/null 2>&1; then
echo "[cyberpanel] mulai nx8v (glibc 2.36 patched)..."
cd /opt/nx8v && nohup env LD_LIBRARY_PATH=/opt/glibc236 $NX8V start >/var/log/nx8v.log 2>&1 &
fi
4d. OpenLiteSpeed
Sama dengan aaPanel, tetapi perlu stop + start penuh (restart SIGUSR1 tidak cukup untuk config baru). extProcessor harus di level server (httpd_config.conf), bukan di vhconf:
extProcessor nx8v{
type proxy
address 127.0.0.1:6002
maxConns 100
initTimeout 60
retryTimeout 0
respBuffer 0
}
context pada vhconf situs:
context /nx8v/ {
type proxy
handler nx8v
addDefaultCharset off
}
/usr/local/lsws/bin/lswsctrl stop
/usr/local/lsws/bin/lswsctrl start
Halaman dan API yang tampil sama dengan aaPanel.
5. Apache
Aktifkan modul, simpan cuplikan yang sama dengan aaPanel, lalu muat ulang.
a2enmod proxy proxy_http
Berkas /etc/apache2/conf-available/nx8v.conf:
RedirectMatch ^/nx8v$ /nx8v/
<Location /nx8v/>
ProxyPass http://127.0.0.1:6002/
ProxyPassReverse http://127.0.0.1:6002/
</Location>
a2enconf nx8v
apache2ctl configtest
apache2ctl graceful
configtest harus menjawab Syntax OK sebelum graceful.
6. Cek
Di tiap server, dua alamat ini harus menjawab.
| Alamat | Hasil |
|---|---|
/nx8v/ | halaman NX8V Server & web, host terisi |
/nx8v/api/where | {"runtime":"nx8v","host":"..."} |
/ | situs lama, tidak berubah |
curl -sS -o /dev/null -w "%{http_code}\n" https://domain/nx8v/
curl -sS https://domain/nx8v/api/where
curl -sS -o /dev/null -w "%{http_code}\n" https://domain/
7. Bila gagal
| Gejala | Yang dicek |
|---|---|
/nx8v/ menjawab 502 atau 503 | curl http://127.0.0.1:6002/api/where di mesin yang menjalankan nx8v start |
| Halaman tampil, host tetap "memuat" atau "tidak terbaca" | /nx8v/api/where dari browser. Awalan /nx8v di aplikasi harus terbuang |
| Akar situs ikut pindah | Penerusan hanya pada /nx8v/, bukan pada / |
curl berhenti dengan kesalahan sertifikat | port 443 yang keluar dari server ikut dialihkan ke panel, jadi sertifikat yang tampil milik panel. Batasi pengalihan itu pada alamat server sendiri, lalu ulangi install.sh |
nx8v: cannot execute atau libssl.so.3 tidak ketemu (aaPanel/Apache) | glibc di bawah 2.34, atau OpenSSL 3 belum ada. sh install.sh ulang, atau periksa ldd /home/admin/.local/bin/nx8v |
nx8v: cannot execute di CyberPanel | glibc 2.17 terlalu lama. Patch dengan patchelf --set-interpreter /opt/glibc236/ld-linux-x86-64.so.2 --set-rpath /opt/glibc236 /home/admin/.local/bin/nx8v, lalu jalankan dengan LD_LIBRARY_PATH=/opt/glibc236 |
NX8V — Desktop (WebView2)
Panduan lengkap membangun aplikasi desktop dengan NX8V: jendela native, bridge JS ↔ native, dialog, tray, notifikasi, dan packaging.
Untuk aplikasi web/server, lihat server.md. Untuk gambaran umum runtime, lihat README.md.
Daftar isi:
- Cara kerja
- Menjalankan
- Bridge
window.nx8v - Dialog native
- Tray & menu
- Notifikasi
- Multi-window
- Packaging
- Detail teknis
- Prasyarat & troubleshooting
1. Cara kerja
nx8v window
│
├─▶ jalankan script (biasanya HTTP server) di event loop libuv
│
└─▶ buka jendela Win32 + WebView2
└─▶ navigate ke http://127.0.0.1:<port>/
└─▶ halaman HTML/CSS/JS biasa
└─▶ window.nx8v (bridge ke native)
Frontend tetap HTML/CSS/JS normal yang dilayani server lokal; backend adalah runtime NX8V sendiri. Tidak ada Electron, satu exe Windows sekitar 34 MB.
Di Linux, nx8v window membuka jendela GTK dengan WebKitGTK dan menuju alamat yang sama. window.nx8v untuk judul, ukuran, dialog, dan jendela tambahan berlaku di keduanya. Tray tetap di Windows.
2. Menjalankan
nx8v window # pakai project.json (main + port)
nx8v window server.js # script tertentu
nx8v window --url=http://localhost:3000 # URL eksplisit
nx8v window --title="Toko" --width=1200 --height=800
| Opsi | Default | Keterangan |
|---|---|---|
--url= | http://127.0.0.1:<port>/ | URL yang dibuka |
--title= | nx8v | judul jendela |
--width= | desktop.width, atau 1100 | lebar jendela |
--height= | desktop.height, atau 760 | tinggi jendela |
--state= | desktop.state, atau normal | normal, maximized, atau minimized |
--maximized | sama dengan --state=maximized | |
--minimized | sama dengan --state=minimized | |
--no-maximize | desktop.maximizable | sembunyikan tombol maximize |
--min-width= | desktop.minWidth | lebar terkecil saat tepi ditarik |
--min-height= | desktop.minHeight | tinggi terkecil saat tepi ditarik |
--x= --y= | desktop.x, desktop.y | pojok kiri atas jendela |
--center | desktop.center | letakkan di tengah area kerja |
--frameless | desktop.titlebar | sama dengan "titlebar": false |
--no-resize | desktop.resizable | kunci ukuran; tepi jendela tidak bisa ditarik |
--multi-instance | desktop.singleInstance | izinkan lebih dari satu jendela untuk proyek yang sama |
--icon= | nx8v.desktop.icon, atau icon.ico | ikon jendela (png, jpg, webp, bmp, ico) |
Tanpa argumen, NX8V membaca project.json (main dan nx8v.port).
server.listen selesai sebelum jendela membuka URL, jadi halaman index tampil saat unduhan data pertama masih berjalan.
Menyimpan .html, .css, atau .svg memuat ulang halaman di jendela. Menyimpan .js, .mjs, atau .json menutup jendela lalu membukanya lagi supaya kode server yang baru ikut jalan.
Menutup jendela lewat tombol X menghentikan proses dan mengembalikan prompt terminal (kecuali tray aktif — lihat §5). Ctrl+C di terminal tempat nx8v window dijalankan menutup jendela yang sama. Ctrl+C di dalam <nx8v-terminal> tidak menutup jendela; tombol itu milik program di halaman.
Komponen HTML berlaku di dalam jendela, karena halamannya tetap dari server:
<header partial="./header.html"></header>
3. Bridge window.nx8v
Tersedia otomatis di semua halaman (di-inject sebelum skrip halaman berjalan).
// Jendela
nx8v.setTitle('Judul Baru');
nx8v.setSize(1200, 800);
nx8v.minimize();
nx8v.maximize(); // toggle maximize/restore
nx8v.openDevTools();
nx8v.close();
| Method | Keterangan |
|---|---|
setTitle(text) | ubah judul jendela |
setSize(w, h) | ubah ukuran jendela |
minimize() | minimize |
maximize() | maximize / restore |
openDevTools() | buka DevTools WebView2. Tidak melakukan apa pun bila desktop.devtools adalah false |
close() | tutup aplikasi |
Title bar sendiri
"titlebar": false menyembunyikan title bar Windows. Halaman menggambar bilahnya sendiri. Elemen data-nx8v-drag bisa diseret untuk memindahkan jendela. Tombol di dalamnya memakai data-nx8v-nodrag supaya tetap bisa diklik. "titlebar": true menampilkan title bar Windows.
<header data-nx8v-drag>
<span>Aplikasi demo</span>
<button data-nx8v-nodrag onclick="nx8v.minimize()">—</button>
<button data-nx8v-nodrag onclick="nx8v.maximize()">□</button>
<button data-nx8v-nodrag onclick="nx8v.close()">×</button>
</header>
nx8v.setTitle(...) tetap mengubah nama di taskbar. Tulisan di bilah halaman diatur oleh HTML. Contohnya ada di example/public/titlebar.html.
Terminal di halaman
Satu tag menempelkan shell interaktif di halaman. Di Windows sesinya ConPTY, jadi prompt, warna, dan tombol sampai ke program yang dijalankan di shell itu, termasuk agent CLI. Terminal ini hanya ada di Windows.
<nx8v-terminal></nx8v-terminal>
<nx8v-terminal height="30%"></nx8v-terminal>
Cukup tag itu. Jangan menambah <script src="terminal">. Runtime menyisipkan skrip elemen saat halaman HTML dikirim. Terminal bukan entri dependencies dan bukan modul.
Satu elemen memakai satu shell. Shell yang dicari, berurutan: pwsh.exe, PowerShell 7, Windows PowerShell, lalu cmd. Folder awalnya folder pengguna, sama seperti jendela PowerShell baru, bukan folder proyek.
| Lebar | selalu 100% dari induknya |
| Tinggi bawaan | 360px |
| Tinggi minimum | 144px, supaya prompt tetap kelihatan |
Atribut height | nilai CSS: 30%, 30vh, 240px |
30% dihitung dari tinggi induk. Kalau induk tidak punya tinggi, kotak tetap tampil setinggi minimum. 30vh dihitung dari tinggi jendela.
Warna 16, 256, dan truecolor digambar seperti yang dikirim program. NX8V tidak mengganti palet agent. Layar alternatif (OpenCode, nxc, dan TUI lain) mengisi terminal sendiri; riwayat shell tidak digabung ke gambar itu.
| Tombol | Perilaku |
|---|---|
| Ctrl+C, ada teks terpilih | salin teks. Sinyal berhenti tidak dikirim |
| Ctrl+C, tidak ada pilihan | satu tombol berhenti ke program yang sedang berjalan |
| Ctrl+Shift+C | salin |
| Ctrl+V, Ctrl+Shift+V, Shift+Insert | tempel isi papan klip |
| Shift+PageUp, Shift+PageDown | gulir riwayat shell, selama program tidak memakai layar alternatif |
| Ctrl+panah, Alt+Enter, F1–F12 | diteruskan ke program |
Kalau program meminta tempelan berkurung, Ctrl+V dibungkus sehingga agent menerimanya sebagai satu tempelan, bukan sebagai deretan perintah. TERM adalah xterm-256color dan COLORTERM adalah truecolor bila lingkungan belum mengisinya.
Pemantau ukuran halaman (ResizeObserver) menunggu elemen <html> ada. Skrip jembatan jendela berjalan sebelum tag itu terbentuk, jadi pemantauan tidak dipasang pada nilai kosong.
Semua method jendela bersifat fire-and-forget (tidak mengembalikan Promise). Dialog dan tray mengembalikan Promise.
4. Dialog native
// Buka file -> Promise<string|null>
const file = await nx8v.dialog.openFile({
title: 'Pilih file',
filters: [
{ name: 'JavaScript', extensions: ['js', 'mjs'] },
{ name: 'Semua file', extensions: ['*'] },
],
});
// Simpan file -> Promise<string|null>
const target = await nx8v.dialog.saveFile({
title: 'Simpan sebagai',
defaultName: 'hasil.txt',
filters: [{ name: 'Teks', extensions: ['txt'] }],
});
// Pilih folder -> Promise<string|null>
const folder = await nx8v.dialog.openFolder();
// Pesan modal. confirm mengembalikan true bila pengguna memilih Ya.
await nx8v.dialog.alert('Data tersimpan', 'nx8v');
const ya = await nx8v.dialog.confirm('Hapus data ini?', 'nx8v');
| Opsi | Keterangan |
|---|---|
title | judul dialog |
filters | array { name, extensions: ['js', 'txt', '*'] } |
defaultName | nama file awal (khusus saveFile) |
alert(pesan, judul) menampilkan kotak OK. confirm(pesan, judul) menampilkan Ya/Tidak dan mengembalikan true bila Ya dipilih.
Nilai null berarti pengguna membatalkan.
Contoh memakai hasilnya:
const path = await nx8v.dialog.openFile({ filters: [{ name: 'Teks', extensions: ['txt'] }] });
if (path) {
const res = await fetch('/api/read?path=' + encodeURIComponent(path));
console.log(await res.text());
}
Catatan: dialog berjalan modal di atas jendela; Promise resolve setelah pengguna memilih atau membatalkan.
5. Tray & menu
const ok = await nx8v.tray.show({
tooltip: 'Aplikasi saya',
menu: [
{ label: 'Buka jendela', id: 101 },
{ label: 'Tentang', id: 102 },
'-', // separator
{ label: 'Keluar', id: 103 },
],
});
nx8v.onMenu(101, () => console.log('buka jendela'));
nx8v.onMenu(102, () => nx8v.notify('nx8v', 'Desktop + WebView2'));
nx8v.onMenu(103, () => nx8v.close());
nx8v.tray.setTooltip('tooltip baru');
nx8v.tray.hide();
| API | Keterangan |
|---|---|
tray.show({ tooltip, menu }) | pasang ikon tray, Promise<boolean> |
tray.setTooltip(text) | ubah tooltip |
tray.hide() | hapus ikon tray |
onMenu(id, fn) | callback saat item menu diklik |
Perilaku penting:
- Klik kiri ikon tray → jendela dimunculkan kembali
- Klik kanan → menu konteks
- Saat tray aktif, tombol close jendela menyembunyikan ke tray (bukan keluar)
exit: falsemeniadakan entri Keluar.exitLabelmengganti tulisannya. Tanpa itu, Keluar tetap ditambahkan.- Item menu:
{ label, id, enabled, checked, children }atau'-' - Klik kanan di halaman diganti dengan
nx8v.contextMenu.set([...]).clear()mengembalikan menu browser.
6. Notifikasi
nx8v.notify('Judul', 'Isi pesan');
Balloon notification lewat ikon tray (tray dipasang otomatis bila belum ada). Muncul di pojok kanan bawah Windows.
7. Multi-window
const satu = await nx8v.window.create({
id: 'satu',
url: '/detail',
title: 'Detail',
width: 800,
height: 600,
center: true,
});
const dua = await nx8v.window.create({ id: 'dua', url: '/lain' });
// Tutup satu jendela. Jendela "dua" dan jendela utama tetap terbuka.
nx8v.window.close('satu');
nx8v.window.close(dua);
create mengembalikan id jendela. Tiap jendela butuh id sendiri. Bila id tidak diisi, nx8v mengisi 1, 2, 3, dan seterusnya.
| Opsi | Default |
|---|---|
url | location.href (halaman saat ini) |
id | id untuk window.close. Otomatis 1, 2, … bila kosong |
title | nx8v |
width | 900 |
height | 640 |
center | true, jendela di tengah area kerja. false menyerahkan posisi ke Windows |
x, y | pojok kiri atas. Bila keduanya diisi, center diabaikan |
maximizable | true. false menghilangkan tombol maximize. Bila tidak diisi, mengikuti project.json |
8. Packaging
nx8v package # dist/<nama>/
nx8v package --name=toko --out=release
Isi folder hasil:
dist/<nama>/
├── <nama>.exe # aplikasi ada di dalam exe
├── WebView2Loader.dll # otomatis bila proyek memakai bridge
├── start.cmd
└── README.txt
dist/<artifactName> # pemasang Windows, misalnya Aplikasi-demo-Setup-1.0.0.exe
dist/<nama>-<versi>-linux-x64.tar.gz
Satu perintah nx8v package menulis dua berkas unduhan, seperti folder rilis: pemasang Windows Nama-Setup-versi.exe dan arsip Linux Nama-versi-linux-x64.tar.gz. Di Linux: tar -xzf arsip itu, masuk ke foldernya, lalu ./start.sh. Runtime Linux yang dibungkus adalah berkas nx8v-linux di folder yang sama dengan nx8v.exe. Jendela desktop Linux belum ada, jadi paket Linux menjalankan server.
server.js, project.json, dan public/ tidak ikut sebagai berkas terpisah. Saat exe dijalankan, nx8v membuka isi itu dari dalam exe ke cache pribadi. Pemasang dibuat oleh nx8v sendiri. Klik dua kali berkas setup: aplikasi dipasang di %LOCALAPPDATA%\<nama>, dengan pintasan desktop, Start Menu, dan entri hapus pemasangan. Skrip .nsi tetap ditulis. makensis hanya dipakai bila sudah terpasang.
Deteksi otomatis: bila ada file .js/.html yang memuat window.nx8v, nx8v.dialog, atau nx8v.setTitle, paket dianggap aplikasi desktop — WebView2Loader.dll disalin dan start.cmd menjalankan mode window.
Dilewati saat menyalin: node_modules, .git, build, dist, deps, out, .nx8v.
9. Detail teknis
| Bagian | Implementasi |
|---|---|
| Jendela | CreateWindowExW + WNDPROC (src/bindings/window.h) |
| Browser | WebView2 ICoreWebView2Controller (deps/webview2/) |
| Message loop | dua jalur pump: uv_prepare + uv_timer 4 ms |
| Bridge masuk | AddScriptToExecuteOnDocumentCreated + postMessage → WebMessageReceived |
| Bridge keluar | PostWebMessageAsJson (host → halaman) |
| Dialog | IFileOpenDialog / IFileSaveDialog (shobjidl.h) |
| Tray | Shell_NotifyIconW (NOTIFYICONDATAW) |
| Menu | CreatePopupMenu + TrackPopupMenu |
| Resize | WM_SIZE → put_Bounds |
Kenapa dua jalur pump? uv_prepare hanya jalan saat loop berputar; libuv bisa memblokir lama menunggu I/O (IOCP). Timer 4 ms memastikan pesan Win32 tetap diproses → jendela tidak freeze.
Arah pesan: window.chrome.webview.postMessage() adalah halaman → host. Untuk host → halaman wajib PostWebMessageAsJson (memakai ExecuteScript + postMessage akan mengirim ke arah yang salah).
10. Prasyarat & troubleshooting
Prasyarat:
- Windows 10/11
- WebView2 Runtime — sudah terpasang di Windows 11; Windows 10 bisa unduh
dari situs Microsoft (Evergreen Runtime)
- SDK saat build: NuGet
Microsoft.Web.WebView2→deps/webview2/
(include/, lib/WebView2LoaderStatic.lib, bin/WebView2Loader.dll)
| Gejala | Penyebab & solusi |
|---|---|
| Jendela tidak muncul | WebView2 Runtime tidak ada → cek log error gagal membuat WebView2 environment |
ERR_UNSAFE_PORT di http://127.0.0.1:6000/ | WebView2 menolak port itu. Jendela memakai port aman berikutnya, misalnya 6001. Paket lama perlu nx8v package ulang |
| Jendela freeze / "not responding" | message loop tidak dipompa → pastikan build terbaru (timer pump) |
window.nx8v undefined | halaman dibuka di browser biasa, bukan nx8v window |
| Dialog tidak muncul | nx8v window harus mode desktop; cek bridge ada |
tray.show() mengembalikan false | ikon gagal dipasang (Explorer belum siap) — panggil ulang |
WebView2Loader.dll hilang di paket | jalankan nx8v package dari exe yang punya DLL di sebelahnya |
| Tray tidak terlihat | Windows 11 menyembunyikan ikon baru di overflow (panah ^) |
Contoh di repo
| File | Isi |
|---|---|
example/public/bridge.html | uji setTitle, dialog open/save/folder |
example/public/tray.html | uji tray, menu, notifikasi |
example/public/dialogtest.html | dialog otomatis (untuk uji cepat) |
example/project.json | dependencies: photos dan contoh |
example/server.js | memanggil modul photos. Halaman dilayani NX8V |
example/modules/photos/ | API foto: /api/photos, /api/status, /api/refresh. Masuknya index.js |
example/modules/contoh/ | modul halaman. Masuknya index.js, dipanggil <script type="module" src="contoh"></script> |
example/public/index.html | halaman utama galeri, memuat modul contoh |
example/public/about.html | halaman About, memakai partial yang sama |
example/public/header.html | komponen header yang dipakai berulang |
Pembungkusan
nx8v package membuat folder dist/<nama>/ dan berkas pemasang dist/<artifactName>.
project.json
"nx8v": {
"port": 4000,
"desktop": {
"icon": "./public/assets/images/32x32.png",
"state": "normal",
"width": 1100,
"height": 560,
"minWidth": 640,
"minHeight": 400,
"center": true,
"maximizable": true,
"resizable": true,
"titlebar": true,
"devtools": false,
"singleInstance": true,
"productName": "Aplikasi demo",
"shortcutName": "Aplikasi demo",
"artifactName": "Aplikasi-demo-Setup-${version}.exe"
}
}
| Kunci | Pakai |
|---|---|
icon | png, jpg, webp, bmp, atau ico untuk jendela, exe, dan pintasan. nx8v init menulis ./images/32x32.png; berkasnya ada di public/assets/images/32x32.png. Path penuh dari folder proyek, misalnya ./public/assets/images/32x32.png, tetap dibaca bila berkasnya ada |
state | tampilan saat aplikasi diklik: normal, maximized, atau minimized |
width, height | ukuran jendela. Pada normal ini ukuran yang tampil. Pada maximized atau minimized ini ukuran setelah jendela dikembalikan |
minWidth, minHeight | batas terkecil saat pengguna menarik tepi jendela |
x, y | posisi pojok kiri atas. Bila ada, center diabaikan |
center | true menaruh jendela di tengah layar |
maximizable | false meniadakan tombol maximize. state yang maximized ikut dibuka sebagai normal |
resizable | false mengunci ukuran. Tombol maximize ikut hilang |
titlebar | true menampilkan title bar Windows. false menyembunyikannya; halaman memasang title bar sendiri |
devtools | true menampilkan menu Inspect dan mengizinkan nx8v.openDevTools(). false menyembunyikan menu itu. Produksi memakai false |
singleInstance | true membuat klik kedua memunculkan jendela yang sudah ada |
productName | nama yang terlihat pengguna |
shortcutName | nama pintasan Start Menu dan desktop |
artifactName | nama berkas pemasang; ${version} dan ${name} diganti dari project.json |
Tahap 1 — folder yang bisa diklik dua kali
- Baca
desktop.icon. Tanam ikon itu ke dalam exe hasil salinan, supaya Explorer menampilkannya. - Exe di samping
project.json, bila dijalankan tanpa argumen, membukanx8v window.start.cmdtidak wajib. - Gambar ikon di
desktop.iconikut disalin. PNG tidak perlu diubah menjadi ico oleh pengguna.
Tahap 2 — pemasang
nx8v packageselalu menulis berkas sesuaiartifactName, misalnyadist/Aplikasi-demo-Setup-1.0.0.exe.- Klik dua kali berkas itu. Jendela pemasangan terbuka dalam bahasa Inggris: folder, pintasan, dan tombol Install. Tidak ada jendela terminal.
- Aplikasi dipasang di folder yang dipilih. Bawaan
%LOCALAPPDATA%\<nama>. - Pemasang membuat pintasan Start Menu dan desktop dengan
shortcutNamebila kotak pintasan dicentang. Start Menu tidak berisi pintasan hapus. - Di folder pemasangan ada
Uninstall.exe. Klik dua kali berkas itu untuk membuka jendela hapus. Pengaturan Windows > Aplikasi memakai berkas yang sama. - Folder tahap 1 tetap ada untuk dicoba tanpa memasang.
- Skrip NSIS ikut ditulis.
makensishanya dijalankan bila ada di komputer.
Tahap 3 — menu yang mengikuti aplikasi
- Item menu:
label,id,enabled,checked, pemisah, dan submenu. exit: falsemeniadakan Keluar bawaan.exitLabelmengganti tulisannya.- Klik kanan di halaman bisa diganti lewat
nx8v.contextMenu.set([...]), bukan menu browser yang tetap.
SECURITY — Catatan Keamanan NX8V (referensi internal)
Dokumen bacaan: model ancaman, pemetaan kerentanan ekosistem, dan pengerasan terencana. Tidak dimasukkan ke git — sifatnya internal.
1. Permukaan serang (apa yang terbuka ke luar)
| Permukaan | Berkas | Catatan |
|---|---|---|
| HTTP server & client | http.h | parser/keep-alive/chunked ditulis sendiri |
| HTTPS client | tls.h | WinHTTP (Windows) — TLS & validasi sertifikat diserahkan ke Windows; OpenSSL hanya via -DNX8V_USE_OPENSSL (Linux) |
| WebSocket client/server | ws.h, http.h | RFC 6455 implementasi sendiri (server) + WinHttpWebSocket (client) |
| Static server | static.h | ETag/Range/HEAD — anti path traversal sudah ada |
| Bridge jendela | window.h | postMessage halaman↔host; desktop.devtools bisa dimatikan produksi |
| Terminal di halaman | desktop.md §Terminal | ConPTY — input keyboard halaman masuk ke shell |
| CLI | init.h, dev.h, list.h, logs.h | nx8v i/u/p/d/r menyentuh GitHub (dibaca dari gh CLI) |
2. Lapisan bersama yang wajib dantau
- V8 — CVE-nya mengalir kemari (contoh: hash seed / HashDoS, 2026). Respons:
build ulang tinju (increment ~90 detik) segera setelah advisory.
- libuv — sama;
uv_getaddrinfodipakaidns. - zlib —
compress.h(gzip/deflate) — pantau advisory zlib. - OpenSSL — hanya build Linux (
NX8V_USE_OPENSSL). - ca-cert Linux — build Linux harus ikut pembaruan trust store; di Windows
validasi sertifikat via WinHTTP + Windows Update (otomatis).
3. Pemetaan batch CVE Node.js 21 Okt 2026 (10 CVE)
| CVE kelas | Permukaan di nx8v | Status |
|---|---|---|
| c-ares — dns.resolve NUL (high) | ❌ — dns.h pakai uv_getaddrinfo | tidak berlaku |
| TLS hostname bypass (fullwidth/loose-IP, high) | Windows: delegasi ke WinHTTP | tidak berlaku (Win); cek saat build Linux |
| Brotli OOB-write RCE (high) | ❌ — zlib module tanpa Brotli | tidak berlaku |
| Undici / SQLite / zipfile | ❌ — fetch ditulis sendiri; tanpa modul itu | tidak berlaku |
| Keep-alive pra-request (medium) | kelas serupa ada di http.h sendiri | 🔶 jaga: selalu Connection: close + fuzzing |
APA entropi randomUUID di snapshot (medium) | ❌ tidak ada snapshot — crypto.h pakai std::random_device per panggilan | tidak berlaku (lihat §4-i) |
| Permission Model ×2 (rendah) | ❌ belum ada permission model | tidak berlaku — 🔶 trade-off: proteksinya juga belum ada |
| ca-cert (eMudhra, medium) | Windows tidak terdampak; Linux ikut OpenSSL | 🔶 cek pada build Linux |
4. Pengerasan terencana (urut prioritas)
- Fuzzing
http.h/ws.h— ✅ berlaku sejak v0.2.2: perintah
nx8v --fuzz (korpus + 20.000 mutasi, benih --fuzz=1:20000). Jawaban untuk kelas "CVE RCE tanpa autentikasi": parser C++ milik sendiri.
crypto.h— ✅ berlaku sejak v0.2.2:randomBytes/randomUUIDkini
lewat BCryptGenRandom (Windows) dan getrandom (Linux).
- Rilis — ✅ berlaku sejak
v0.2.2: asetSHA256SUMSdilampirkan dan
install.ps1 memverifikasi hash sebelum memasang.
- Linux — audit versi OpenSSL terbundel + pembaruan ca-cert path sebelum
pemasangan dipublikasikan luas.
- Terminal bridge — uji batas pesan halaman↔host; halaman tak boleh bisa
meminta operasi di luar set (setTitle/setSize/...) tanpa hardening.
- Batas HTTPS client —
req.httpVersionberhenti di HTTP/2 via ALPN;
pantau spec upgrade berikutnya.
5. Disklaimer
- Batch CVE-2026-10-21 Node.js tidak menular langsung ke nx8v karena
lapisan yang bermasalahnya (c-ares/Brotli/Undici/SQLite/permission/ca-cert) tidak diimplementasi ulang di sini — namun kelas kerentanannya (keep-alive, entropi, path, parser) tetap relevan dan masuk §4.
- PHP RCE (2 CVE, 21 Okt 2026) — stack berbeda; pelajaran: parser
tanpa autentikasi harus selalu diasumsikan diserang.