Identitas halaman mirror: marker tersembunyi, bukan nama
Pengikatan halaman mirror lewat nama itu rapuh; modul pagesync memindahkan otoritas ke marker HTML tersembunyi plus sha256 konten yang dinormalisasi.
Ringkasan
Alat pagesync v3 mirror file markdown ke Plane Pages pakai komen HTML tersembunyi sebagai identitas, bukan nama halaman yang gampang diubah editor. Aturannya ketat, kalau marker hilang langsung diblok, file twoway menang lawan oneway, dan oneway cuma bisa publish aja. Drift dicek pakai sha256 tanpa frontmatter plus validasi config yang galak biar nggak kena path traversal.
Marker Tersembunyi sebagai Otoritas
Jari saya udah ngetik nama halaman sebagai pengikat pas nulis modul pagesync buat alat sinkronisasi dokumen. Untung saya berhenti. Nama halaman itu buat manusia: editor bisa mengubahnya kapan aja, dan begitu berubah, pengikatan berbasis nama fork diam-diam. Identitas yang dibentuk oleh konten jauh lebih tahan banting. Versi 3 alat ini nunjukkin pola desainnya dengan bersih.
Konteksnya: alat ini mirror file markdown di .docs ke Plane Pages self-hosted. Versi 3 nambahin mesin halaman di sebelah mesin work-item yang udah terbukti. Primitif pertama yang saya tulis: identitas. Solusinya marker HTML tersembunyi di baris pertama body halaman, isinya path relatif file sumber. Bentuknya begini:
<!-- plane-doc-sync:v3 path=.docs/MEETING/2026-09-27-weekly.md -->
# Meeting Notes
Actual content starts here
Komen HTML memang didesain buat catatan penjelas atau buat nahan browser menginterpretasikan bagian dokumen, dan browser nggak pernah merendernya [1]. Probe langsung di hari dan malam sebelumnya nunjukkin Plane Community Edition melakukan round-trip HTML halaman secara verbatim, komen termasuk. Dokumentasi resmi Plane memperingatkan bahwa mereka melakukan sanitasi HTML di sisi server [2], jadi desain marker ini ngandelin perilaku CE yang terukur, dengan dokumentasi sebagai batas konservatifnya. Marker hilang atau salah format? Halaman langsung diklasifikasikan blocked. Alat ini nggak menebak. Path di marker harus sepakat dengan path item di config; page_id boleh dicatat di config sebagai handle buat panggilan API, tapi dia nggak pernah jadi pengikat.
Kenapa disembunyikan? Di versi 2, marker hidup di work-item yang HTML-nya memang disanitasi server, jadi komen langsung terkelupas. Halaman nggak melakukan itu. Bentuk penyimpanan menentukan bentuk identitas.
Penamaan untuk Manusia, Tier yang Tegas
Penamaan halaman dibikin begini: MEETING: YYYY-MM-DD — buat file dua arah, DOC: buat dokumen perpustakaan satu arah, misal file rapat di .docs/MEETING/2026-09-27-weekly.md jadi halaman bernama MEETING: 2026-09-27 — Weekly. Judulnya diambil dari frontmatter kalau ada, kalau nggak ada, dirangkai dari tanggal plus slug file. Namanya enak dibaca manusia, dan perannya berhenti di situ. Karena halaman di Plane itu datar, proses create mengabaikan parent, struktur hidup di dalam nama, bukan di hierarki folder.
Resolusi tier-nya juga sengaja dibikin bebas ambiguitas. File yang jatuh di bawah direktori twoway sekaligus oneway dianggap twoway, titik; keputusan ini disengaja biar satu file nggak pernah punya dua perlakuan. Di luar keduanya, error, bukan tebakan. Halaman oneway sifatnya publish-only, proses pull ditolak oleh desain, karena arahnya emang cuma satu: dari repo ke halaman. Identitas konten yang jadi otoritas bikin aturan kayak gini gampang dijalanin: status halaman ditarik dari marker plus hash, bukan dari perasaan.
Drift dan Validasi Config
Otoritas drift dihitung pakai sha256 atas markdown yang udah dinormalisasi di kedua sisi. Di sisi repositori, YAML frontmatter dibuang dulu sebelum di-hash, jadi metadata lokal nggak pernah memengaruhi hasil drift. Di sisi Plane, hash dihitung dari markdown yang ditarik balik lewat converter. Python nyediain sha256 sebagai konstruktor yang selalu ada di modul hashlib, meski dokumentasinya nyertain catatan FIPS bahwa beberapa fungsi hash punya ketahanan kolisi yang lebih rendah dari harapan [3]. Makanya validator config saya pin algoritmanya eksplisit, nggak percaya default ambient.
Validasi config juga punya garis keras. Setiap path item harus resolve di dalam repositori. Path traversal itu kelas bug OWASP tersendiri, dan di sini dikategorikan bug config: hard stop [4]. Field last_sync wajib megang dua digest hex 64 karakter plus timestamp. Config v2 yang nyoba nyentuh aksi halaman langsung mati dengan satu pesan error yang persis sama tiap kali.
Pelajaran dari modul kecil ini: identitas yang dibentuk konten bertahan melewati rename, penulisan ulang editor, dan pergantian ID. Identitas yang dibentuk nama atau judul bakal patah di kesempatan pertama ada yang mengganti nama.