Platform Operasional Sendiri: dari Brankas ke SPT Coretax
Fajrian Aidil Pratama 12 menit baca
Banua Coder, perusahaan perangkat lunak kecil di Palu, menjalankan rilis, rahasia, kontrak, tanda tangan, dan pajak dengan satu cara kerja yang kami bangun sendiri. Kami merilis aplikasi keuangan seluler, starter kit Flutter, layanan telemetri, API publik, pelacak bus, dan situs web kami sendiri. Tim sekecil kami hanya bisa menjaga semua itu tetap berjalan bila caranya seragam.
Kami mulai dari cara setiap produk dirilis, lalu masuk ke lima bagian: brankas rahasia untuk orang dan agen AI, platform operasional yang menjalankan perusahaan, dokumen bertanda tangan yang bisa diverifikasi siapa saja, panduan mengisi SPT Tahunan di Coretax, dan apa yang dilihat klien saat kami bekerja.

Satu jalur rilis untuk setiap produk
Setiap repositori mengikuti enam tahap yang sama. Alatnya berbeda menurut stack, tetapi jalurnya tidak.
Rencana
Pekerjaan dimulai sebagai tugas di Beads, pelacak isu kami, atau sebagai isu GitHub. Catatan tim tersimpan di Obsidian. Agen AI membantu, dan aturannya spesifikasi lebih dulu: spesifikasi singkat ditulis sebelum kode.
Bangun
Pekerjaan dilakukan di branch fitur dan masuk ke develop lewat pull request. Pemeriksaan otomatis datang dari repositori workflow GitHub Actions milik kami yang dipakai ulang di semua repositori dan dikunci ke satu tag versi. Ada satu workflow untuk setiap stack: Go, Flutter, Laravel, Node, dan Rust.
Rilis
Kami memakai Git Flow. Branch release/x.y.z menaikkan versi dan menulis changelog. Sebuah pull request membawanya ke main. Penggabungan membuat tag versi dan rilis GitHub. Lalu pull request penggabungan balik membawa commit rilis kembali ke develop.
Publikasi
Setiap artefak masuk ke registri-nya sendiri. Image Docker dibangun di GitHub Actions dan diunggah ke registri kontainer GitHub. Paket Dart dan Flutter masuk ke pub.dev, paket JavaScript ke npm, paket PHP ke Packagist, dan brick Mason kami ke Cloudflare R2. Situs statis disalin ke shared hosting.
Deploy
GitHub Actions terhubung ke server lewat SSH. Server menarik image dari registri, jadi runner tidak pernah mengirim byte image ke server. Docker Compose menjalankannya, dan skrip menunggu sampai kontainer sehat. Lalu smoke test memeriksa situs yang hidup. Bila ada yang gagal, skrip kembali ke versi sebelumnya. Lumbung dan Archipelago memakai deploy blue/green. Traefik memberi HTTPS ke setiap aplikasi. Aplikasi berbagi beberapa layanan di server yang sama: Postgres dan MySQL untuk data, Redis untuk cache, antrean, dan sesi, serta broker MQTT bersama untuk aplikasi realtime.
Pantau
Layanan telemetri kami sendiri, beacon, mengirim data lewat Grafana Alloy ke Grafana Cloud. Dari sana kami mendapat probe uptime dan dashboard. Cadangan harian masuk ke penyimpanan cloud. Rilis Archipelago mengirim pemberitahuan ke Discord.
Satu aturan menjaga semua ini jujur: tidak ada yang membangun image di laptop lalu menyalinnya ke server. Kami pernah melanggarnya pada platform operasional yang dijelaskan di bawah. Setelah menyadarinya, kami memindahkannya ke jalur yang sama, dan sekarang ia dirilis seperti produk lain.
Bagian 1: Brankas rahasia untuk tim dan agen AI
Masalah pertama di tim mana pun adalah rahasia. Kunci tersimpan di pesan obrolan, di berkas .env yang disalin manual, dan di catatan bersama. Saat seseorang keluar, kuncinya tetap ada.
Kami menjalankan Vaultwarden sendiri. Ia memakai protokol Bitwarden, jadi staf memakai alat baris perintah resmi bw. Lima aturan membentuk desainnya:
- Satu catatan aman untuk setiap proyek dan lingkungan, dengan nama seperti
lumbung/.env.staging. Setiap kolom di dalam catatan adalah satu variabel, dengan nama variabel sebagai nama kolomnya. - Akses diberikan per koleksi. Setiap orang hanya melihat proyek yang ia kerjakan. Untuk mencabut akses, pemilik menghapus orang itu dari koleksi, lalu merotasi rahasia yang bisa ia baca.
- Satu perintah menarik catatan ke berkas lokal. Kami tidak pernah meng-commit berkas itu.
- Installer-nya bertanda tangan. Pemilik menandatangani skrip dengan kunci yang tidak pernah berada di server. Installer menolak memasang apa pun kecuali tanda tangannya valid dan setiap berkas cocok dengan SHA-256 di daftar bertanda tangan.
.envdi server dipasang dari catatan brankas dan tidak pernah diubah manual. Perintahbw-env-deploylebih dulu menampilkan apa yang akan berubah, berdasarkan nama variabel, tanpa mencetak nilainya. Perintah ini tidak menulis apa pun sampai Anda menambahkan--apply.
Cara engineer atau freelancer mendapatkan berkas .env proyek
Engineer atau freelancer baru hanya butuh .env satu proyek dan tidak lebih. Untuk itu kami membuat tiga perintah kecil di atas Bitwarden CLI. Diagram di bawah menunjukkan seluruh jalurnya, langkah demi langkah.

- Pemilik mengundang orang itu ke brankas dan menambahkannya ke koleksi proyek tersebut. Saran kami untuk freelancer: hanya koleksi staging, dengan kunci berhak akses rendah.
- Orang itu menjalankan installer bertanda tangan dari situs platform operasional kami:
curl -fsSL https://<ops-host>/install.sh | sh. Installer memasang Bitwarden CLI dan perintah kami, dan berhenti bila tanda tangan pemilik atau hash berkas mana pun tidak cocok. - Orang itu menjalankan
bw login. Kunci sesi tersimpan di Keychain macOS, jadi tidak ada permintaan kata sandi setiap kali. - Orang itu menjalankan
bw-env lumbung staging. Perintah menulis.env.stagingdi folder proyek, dan berkas itu tidak pernah di-commit. - Saat pekerjaan selesai, pemilik mengeluarkan orang itu dari koleksi dan merotasi kunci yang bisa ia baca.
Ketiga perintah itu: bw-env menarik catatan ke berkas, bw-env-push membuat catatan dari berkas .env yang sudah ada, dan bw-env-deploy menyamakan berkas di server dengan catatan. bw-env-push membuat satu kolom tersembunyi per variabel dan tidak pernah menimpa catatan. Dengan --update, perintah menampilkan nama variabel yang ditambah, diubah, dan dihapus sebelum mengubah apa pun.
Pipeline kami memakai akun brankas terpisah dengan akses baca saja ke satu koleksi bernama CI. Koleksi itu hanya berisi yang dibutuhkan pipeline, tidak pernah rahasia produksi. Sebuah GitHub Action kecil mengubah setiap kolom catatan menjadi variabel lingkungan yang disamarkan.
Sekarang soal agen AI. Mereka bekerja di shell yang sama dengan developer, jadi mereka menghadapi risiko yang sama. Aturan kami: agen boleh memakai nilai, tetapi tidak pernah membaca atau mencetaknya. Agen menjalankan bw-env, berkas jatuh ke disk, dan hanya program yang membaca berkas itu. Nilainya tidak masuk ke prompt atau obrolan. Dry run hanya menampilkan nama, dan itu cukup bagi agen untuk menalar sebuah perubahan.
Ini aturan, bukan tembok. Agen yang bisa menjalankan perintah shell bisa membaca berkas apa pun yang bisa dibaca developer. Karena itu akses dibatasi per proyek, dan kami merotasi rahasia setiap kali ada keraguan.
Bagian 2: Satu platform operasional untuk seluruh siklus klien
Kami tidak menemukan alat yang menangani praktik Indonesia sekaligus. Praktik itu mencakup pemotongan PPh 23 oleh klien, kurs KMK, bea meterai, dan dokumen serah terima BAST dan SPPT. Karena itu kami membangun platform operasional. Platform ini satu program Go dengan antarmuka Vue di dalamnya. Datanya ada di database Postgres bersama, dan proses masuknya lewat penyedia identitas kami sendiri, Rauthy. Kami pernah menulis tentang satu login untuk banyak aplikasi internal.
Platform ini mengikuti satu klien dari kontak pertama sampai invoice terakhir:
- Klien, dengan portal tempat klien masuk dan melihat proyeknya.
- Proposal, NDA, dan kontrak, dibuat dari template dalam Bahasa Indonesia atau Inggris.
- Invoice dalam beberapa mata uang, dengan PPh 23 dan kurs KMK pada tanggal terbit.
- Buku besar berpasangan (double-entry). Entri tidak pernah diedit. Koreksi dibuat sebagai pembalikan.
- Proyek dengan tahap sebelum pekerjaan, tahap sesudahnya, dan milestone.
Dokumen saling terhubung: NDA, proposal, kontrak, invoice, BAST, SPPT. Dari dokumen mana pun Anda bisa membuat dokumen berikutnya dengan data yang sudah terisi. Saat kontrak ditandatangani, platform membuat dokumen onboarding dan mengundang klien ke portal. Saat proyek selesai, platform membuat dokumen serah terima.

Email sungguhan hanya terkirim bila APP_ENV=production, dan hanya server yang menyetelnya. Di tempat lain platform memakai kotak surat sandbox yang menangkap setiap pesan. Laptop atau tes tidak bisa mengirim email ke klien.
Bagian 3: Tanda tangan elektronik dengan jejak audit publik
Awalnya kami melirik layanan tanda tangan yang di-host pihak lain. Fitur embed-nya berbayar, dan layanan itu butuh sekitar 2 GB memori di server kecil kami. Jadi kami membangun fitur tanda tangan di dalam platform.
Diagram di bawah menunjukkan alurnya. Setiap penanda tangan menerima tautan pribadi. Jika dokumen mencantumkan nilai di atas Rp5.000.000, dokumen itu butuh bea meterai. Pemilik mengetik nomor seri dari Peruri, karena platform tidak memanggil Peruri. Setiap tindakan masuk ke jejak audit yang hanya bisa ditambah: siapa, kapan, dari alamat mana, dan dari perangkat apa.
Sebagian dokumen perlu segel dari pihak ketiga, misalnya e-meterai atau tanda tangan tersertifikasi. Segel itu dipasang pada PDF sebelum langkah terakhir. Langkah terakhir selalu sama: platform menyimpan sidik jari berkas yang sudah jadi dan mengirimnya ke OpenTimestamps, dan tidak ada yang mengubah berkas setelah itu. Saat ini pemilik mencatat nomor seri e-meterai secara manual. Langkah yang membawa berkas bermeterai kembali ke platform akan menyusul.

Saat penanda tangan terakhir selesai, platform membuat PDF final. Platform menambahkan halaman sertifikat dan kode QR, lalu menyimpan sidik jari SHA-256 dari berkas itu. Sidik jari tersebut dikirim ke kalender publik OpenTimestamps. Kalender itu menjangkarkannya di sebuah blok Bitcoin dalam sekitar sehari. Setelah itu, tidak ada yang bisa mengklaim berkas itu dibuat lebih lambat dari blok tersebut.
Kode QR membuka halaman verifikasi publik. Halaman itu menampilkan nomor dokumen, penanda tangan, sidik jari, dan status cap waktu. Judul disembunyikan kecuali pemilik mengizinkannya, karena judul sebuah NDA bisa bersifat rahasia.


Kami pernah membuat satu kesalahan di sini. Awalnya halaman menampilkan tanda “valid” hijau. Padahal halaman itu memeriksa salinan yang disimpan server kami, bukan PDF di tangan pembaca. PDF yang diubah dengan kode QR yang masih berfungsi tetap akan menuju halaman hijau. Sekarang halaman itu menulis “salinan kami masih utuh”. Sebuah formulir memungkinkan pembaca mengunggah PDF mereka sendiri. Server menghitung hash-nya dan membandingkannya byte demi byte, lalu membuang berkasnya.
Ada satu batasan yang perlu kami akui. Ini tanda tangan elektronik tidak tersertifikasi menurut hukum Indonesia (UU ITE). Tanda tangan ini sah, tetapi bobot pembuktiannya lebih rendah daripada tanda tangan dari penyedia tersertifikasi. Pengacara harus meninjau setiap template. Kami menyatakan ini di dalam platform.
Bagian 4: Panduan mengisi SPT Tahunan di Coretax
Bagian ini yang paling kami banggakan.
Setiap tahun, pelaporan SPT adalah pekerjaan paling berat. Formulir lama DJP berupa formulir XFA dari Adobe. Di Mac formulir itu menampilkan layar kosong “Please wait”. Kami menulis parser untuknya dan mengisinya dari pembukuan kami. Lalu DJP memindahkan pelaporan ke Coretax, sistem web dengan formulir panjang. Jadi kami mengganti targetnya.
Halaman Tax forms sekarang membuat semuanya dari buku besar untuk tahun yang Anda pilih:
- Panduan Coretax. Panduan ini mengikuti layar Coretax pertanyaan demi pertanyaan, dengan jawaban yang diketik di samping setiap pertanyaan.
- PDF pendukung: laporan keuangan, neraca saldo, jurnal umum, buku besar, rincian biaya, rekapitulasi pendapatan, kredit pajak PPh 23, dan biaya dibayar di muka.
- Berkas zip berisi CSV untuk konsultan pajak.
- Angka tahunan, termasuk kerugian fiskal yang dikompensasikan dari tahun sebelumnya.


Platform tahu rezim pajak mana yang berlaku di setiap tahun dan menampilkan angka untuk rezim itu. Platform tidak pernah melaporkan apa pun. Setiap angka diberi tanda “verifikasi sebelum melapor”, dan orang yang memahami pajak harus memeriksa angkanya sebelum Anda mengirim.
Kami belajar satu hal dengan cara sulit. Coretax menampilkan kesalahan parsing saat satu kolom tidak berisi angka. Panduan sekarang mencetak nol secara eksplisit di setiap sel. Angka negatif dicetak dengan tanda minus, karena tanda kurung pernah membuat kami mengetik tanda yang salah.
Bagian 5: Apa yang dilihat klien kami
Bagi klien, minggu-minggu pertama dengan vendor baru adalah yang tersulit. Mereka tidak tahu apa yang terjadi berikutnya, siapa berutang apa, atau apakah pekerjaan bergerak. Kami ingin klien melihat jawaban ketiga pertanyaan itu tanpa harus bertanya kepada kami.

Semuanya dimulai saat kontrak ditandatangani. Platform membuat portal klien dan mengirim undangan lewat email.

Klien memasukkan alamat email dan menerima kode enam digit. Tidak ada kata sandi yang harus diingat. Halaman pertama yang mereka lihat adalah daftar onboarding. Beberapa langkah tercentang sendiri dari kejadian nyata: kontrak ditandatangani, NDA ditandatangani, invoice pertama dibayar. Langkah lain dicentang klien: memeriksa kontak, bergabung ke grup obrolan. Langkah dari pihak kami, seperti menerima bahan dari klien, juga tampil.

Setelah itu klien bisa memantau pekerjaan. Tab Papan adalah daftar tugas proyek yang sebenarnya. Kami menyimpannya di Beads, dan sebuah skrip kecil menyinkronkan cuplikannya ke portal. Tab itu menampilkan progres, empat kolom, prioritas, siapa mengerjakan apa, dan umpan pembaruan terbaru. Sinkronisasi punya aturan privasi bawaan. Tugas berlabel internal tidak pernah keluar dari sisi kami. Komentar yang diawali internal: dibuang. Email pemilik tidak pernah dikirim. Sebuah tugas hanya tampil bila kami memberinya label client-visible.

Tab Proyek menunjukkan gambaran besarnya: apa yang terjadi sebelum pekerjaan, selama, dan sesudahnya. Tab itu memuat milestone dan tanggal target. Dokumen dan invoice ada di tab masing-masing, dan apa pun yang menunggu klien punya tombol Tanda tangani sekarang. Saat proyek selesai, platform membuat draf dokumen serah terima BAST dan SPPT. Kami meninjau dan mengirimnya, klien menandatangani, dan PDF bertanda tangan membawa kode QR dari Bagian 3.

Enam hal sebelum Anda menirunya
- Mulai dari buku besar. Invoice, laporan, dan formulir pajak semuanya membaca darinya.
- Periksa benda yang dipegang pembaca, bukan hanya salinan Anda sendiri.
- Jangan pernah menampilkan angka pajak hasil hitungan sebagai angka final.
- Buat rilis tetap membosankan dan sama di mana-mana. Satu jalur untuk setiap produk lebih baik daripada satu skrip pintar untuk setiap aplikasi.
- Rancang apa yang dilihat klien pada hari pertama. Daftar yang tercentang sendiri murah dibuat dan menghapus banyak pertanyaan.
- Punya sistem sendiri hanya masuk akal bila Anda sanggup merawatnya. Biayanya adalah waktu kami, bukan langganan.
Banua Coder membangun sistem operasional internal seperti ini untuk perusahaan dan instansi pemerintah. Jika Anda menginginkannya, jadwalkan panggilan di banuacoder.com.
Penulis
Fajrian Aidil Pratama
Founder & Director
Fajrian adalah Founder & Director Banua Coder, software engineer dengan pengalaman di mobile engineering, platform, dan digital product development. Latar belakang ini membentuk cara Banua Coder bekerja: lebih terstruktur dalam memetakan masalah, lebih kritis dalam merancang solusi, dan lebih disiplin dalam delivery.
Baca lebih lanjut dari Ryan →Mulai proyek
Punya sistem yang ingin dibangun?
Jadwalkan diskusi dan ceritakan apa yang ingin Anda luncurkan.