PROTEKSI — Spesifikasi API untuk AI Eksternal

Arsitektur tiket: Worker Cloudflare hanya menerbitkan tiket & menerima hasil analisis (hemat kuota). Transfer isi file berlangsung LANGSUNG dari agent di server domain — tidak melalui Cloudflare.

1. Autentikasi

Setiap request ke /api/ext/* membawa header:

X-DSO-Api-Key: dso_ext_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Key diterbitkan pemilik dashboard, ter-scope, dibatasi request/menit, dapat dicabut kapan saja, dan seluruh penggunaannya ter-audit.

2. Alur standar v3 — PARALEL BERBASIS PARTISI PATH (agent v2.3+)

Satu tiket dipakai SEMUA pekerja paralel. Konsurensi paralel TIDAK dibatasi angka tetap — AI yang menentukan secara ADAPTIF dari respons server (lihat FAN-OUT); payload tidak pernah dieksekusi.

# ① Persiapan (Worker, 2 request)
curl $BASE/api/ext/domains  -H "X-DSO-Api-Key: $KEY"
curl $BASE/api/ext/queue?domain_id=$ID -H "X-DSO-Api-Key: $KEY"   # prioritas: review/gagal-baca
curl -X POST $BASE/api/ext/ticket -H "X-DSO-Api-Key: $KEY" \
  -d '{"domain_id":"","scope":"read,deep","ttl_minutes":120}'   # → $TICKET

# ② Peta korpus MURAH — deep list_only: path+size+mtime TANPA isi
curl -X POST "$AGENT_URL?a=deep" -H "X-DSO-Ticket: $TICKET" \
  -H "Content-Type: application/json" -d '{"list_only":1,"offset":0,"limit":200}'
# ulangi offset=next sampai null → daftar path lengkap

# ③ PARTISI OTOMATIS (sisi AI):
#  - kelompokkan per direktori level-1 (wp-admin/, wp-content/, ...)
#  - grup > 50 file → pecah lagi per level-2
#  - hasil: N partisi (N maks 8). Contoh root ada wp-admin & wp-content
#    → 2 pekerja LANGSUNG jalan bersamaan.

# ④ FAN-OUT: tiap partisi = 1 pekerja paralel (Node):
const work = partition => Promise.all(
  chunk(partition.files, 10).map(batch =>           # wp-admin 10 file → terbaca SERENTAK
    Promise.all(batch.map(f =>
      fetch(AGENT_URL + '?a=read', {method:'POST',
        headers:{'X-DSO-Ticket':TICKET,'Content-Type':'application/json'},
        body:JSON.stringify({path:f.p, max_bytes:65536})
      }).then(retryOn502)                           # backoff 2s,3 percobaan
    ))
  )
);
const hasil = await Promise.all(partisi.slice(0,8).map(work));   # ≤8 paralel

# ⑤ FAN-IN: satu POST verdict (maks 200/request), sertakan akuisisi:
curl -X POST $BASE/api/ext/verdicts -H "X-DSO-Api-Key: $KEY" \
  -d '{"domain_id":"","verdicts":[
    {"path":"wp-admin/x.php","suspicion":92,"reason":"webshell eval",
     "acq":"full","bytes_read":5412,"review":"confirmed"}]}'
# file yang gagal dibaca dilaporkan acq:"failed" — TIDAK PERNAH dianggap bersih.

3. Endpoint referensi

EndpointMethodScopeFungsi
/api/ext/whoamiGETverdictsInfo key, sisa kuota, jumlah pemanggilan 24 jam
/api/ext/domainsGETverdictsDomain terpantau + health + insiden 24 jam
/api/ext/ticketPOSTticketTerbitkan tiket agent (scope: read|deep|inventory, TTL ≤ 120 mnt)
/api/ext/incidentsGETverdictsUmpan insiden (path absolut) — ?domain_id=&min_severity=&limit=
/api/ext/queueGETverdictsv2.0 — antrean kerja: file gagal baca / akuisisi belum lengkap / review terbuka, urut prioritas skor (?domain_id=&limit=)
/api/ext/verdictsPOSTverdictsKirim hasil analisis (maks 200 verdict per request). v2.0: sertakan acq (full|partial|headtail|failed), bytes_read, dan review (cleared|confirmed) — menutup file sebagai tidak mencurigakan WAJIB menyertakan reason tercatat.
$AGENT_URL?a=read|deeptiketDATA PLANE — langsung dari server domain (agent v1.6+)

4. Batasan data plane (agent)

read (agent v2.2+): path dikonfinasi dalam root agent; bertahap — seluruh byte terjangkau: parameter offset (boleh negatif = dari akhir), max_bytes ≤ 64KB per chunk, encoding text|base64 (biner TIDAK ditolak — dikembalikan sebagai content_b64 + magic_hex). Respons menyertakan identitas file (size, sha256, mtime, ctime, inode) dan next_offset untuk melanjutkan; bandingkan identitas antar-chunk untuk mendeteksi file berubah selama sesi. deep: halaman ≤ 50 file per request; korpus kini mencakup PHP + config + file special (tanpa ekstensi, gambar ber-marker PHP, JS obfuscation/checkout, persistence SSH); tiap item menyertakan s/m (identitas) dan potongan tail untuk payload yang disembunyikan di ujung file. Seluruh akses tiket dicatat di log server (.dso-api.log.php di atas webroot).

5. Prompt siap pakai (tempel ke AI eksternal)

Ganti <API_KEY_ANDA> dengan key Anda — atau buat key dari dashboard, prompt lengkap dengan key sudah disertakan otomatis di sana.

Kamu adalah auditor keamanan web senior. Server yang kamu periksa adalah milik
pemilik API di bawah (audit atas aset sendiri). Tugas: periksa isi server, temukan
file berbahaya/anomali, dan laporkan lewat API — TANPA mengubah apa pun.

KREDENSIAL (rahasia — jangan tampilkan ulang di jawabanmu):
  API key  : <API_KEY_ANDA>
  Base URL : https://proteksi-observer.berastogelmobile.workers.dev/api/ext
  Spesifikasi lengkap: https://proteksi-myzerotwo.pages.dev/api-ext.html

ALUR PARALEL WAJIB (file dibaca LANGSUNG dari agent — hemat kuota Worker):
 0. GET /domains + GET /queue?domain_id= (header X-DSO-Api-Key) → catat id,
    agent_url, agent_root, dan item antrean (review terbuka / gagal baca).
 1. POST /ticket {"domain_id":"...","scope":"read,deep"} → $TICKET
    (satu tiket dipakai SEMUA pekerja paralel; minta ulang bila 404).
 2. PETA KORPUS: POST agent a=deep {"list_only":1,"offset":0,"limit":200}
    ulangi offset=next sampai null → daftar path+size+mtime (tanpa isi).
 3. PARTISI OTOMATIS: kelompokkan path per direktori level-1
    (contoh: wp-admin/ & wp-content/ → 2 partisi LANGSUNG jalan bersamaan);
    grup >50 file pecah lagi per level-2 → jumlah partisi menyesuaikan kapasitas.
    Urutan: item antrean + skor tertinggi diproses lebih dulu.
 4. FAN-OUT PARALEL — KONSENSI ADAPTIF, KAMU YANG MENENTUKAN (tanpa angka tetap):
    mulai 2-4 koneksi → ukur latensi & error → NAIKKAN bertahap (dobelkan)
    selama 0 error dan latensi stabil → TURUNKAN 50% begitu muncul
    502/timeout/latensi melonjak → ulangi siklus naik-turun ini;
    hormati situs produksi (jam sibuk / latensi naik = lebih konservatif);
    langit-langit = kapasitas nyata yang teramati, bukan angka hafalan;
    kirim partisi serentak (Promise.all) sesuai konsurensi itu;
    dalam tiap partisi baca file ber-batch 10 sekaligus:
    a=read {"path":"...","offset":0,"max_bytes":65536} ulangi next_offset
    sampai null (seluruh byte); offset negatif = dari ekor;
    encoding:"base64" utk biner (cek magic_hex — PHP setelah header gambar?).
    Bandingkan sha256/size/mtime antar panggilan — berubah = laporkan.
    Retry 502/timeout: backoff 2 detik, maks 3 percobaan; tetap gagal →
    laporkan acq:"failed" (TIDAK PERNAH dianggap bersih).
 5. Analisis tiap file: webshell (eval/system/passthru), obfuscation
    (rantai base64+gzinflate, hex escape, chr chain, blob panjang, nama
    sink dipecah 'ev'.'al'), alur input→sink ($_GET/POST → system/
    file_put_contents/include), fetch→eval, backdoor di file sah, gambar
    polyglot, .htaccess/.user.ini beracun. File CMS normal → skor rendah.
 6. FAN-IN: POST /verdicts {"domain_id":"...","model":"","verdicts":[
      {"path":"...","suspicion":0-100,"reason":"...","recommendation":"...",
       "acq":"full|partial","bytes_read":12345,"review":"cleared|confirmed"}]}
    (maks 200 verdict/request; cleared TANPA reason ditolak sistem).
    skor >= 60 otomatis jadi insiden.
 7. Akhiri dengan ringkasan: konsurensi maksimum yang tercapai & kenapa,
    jumlah partisi & pekerja paralel, file
    diperiksa, cakupan byte terbaca, temuan teratas, item belum tuntas
    (laporan tetap PARSIAL selama ada).
ATURAN KERAS: READ-ONLY; jangan bagikan key/tiket; path verdict = path relatif
persis dari API.

6. Etika & keamanan

API ini milik pemilik domain untuk mengaudit servernya sendiri. Tiket berumur pendek dan ter-scope; secret domain tidak pernah dibagikan; setiap panggilan ter-audit. Jangan pernah menautkan key pada repository publik.