Skip to content
Blog DevOps

Bagaimana Saya Membuktikan Credential Email Siap Sebelum Aplikasi Bergantung Padanya

By Romi Fadli, S.Kom. 21 min read Email & Messaging
On this page
A check list on the left ends with a test token arriving: the message goes out to an SMTP server, is delivered to an IMAP inbox, and is read back read-only with the same token

Pendahuluan

Artikel ini membahas pemeriksaan command-line kecil yang saya jalankan pada satu set credential email sebelum aplikasi mana pun boleh bergantung padanya, beserta penalaran di baliknya. Artikel ini bukan panduan menjalankannya; cuplikan kode di sini hadir untuk menjelaskan kenapa satu keputusan diambil. Bagian yang menarik justru jarak antara "login-nya berhasil" dan "akun ini siap dipakai", karena di jarak itu ternyata ada delapan persoalan terpisah yang perlu ditangani.

A. Apa itu SMTP dan IMAP, dan kenapa satu akun butuh keduanya

Email bergerak lewat dua protokol terpisah yang kebetulan memakai username dan password yang sama. SMTP, Simple Mail Transfer Protocol, adalah cara menyerahkan pesan ke mail server untuk dikirim. Aplikasi memakainya setiap kali mengirim: konfirmasi pendaftaran, reset password, notifikasi. IMAP, Internet Message Access Protocol, adalah cara membaca mailbox. Aplikasi memakainya ketika harus menanggapi email yang masuk — balasan atas tiket, dokumen yang dikirim ke alamat bersama, atau bounce dari pesan yang dikirim sebelumnya.

Kedua protokol berjalan di server berbeda, di port berbeda, dan sering dengan aturan berbeda. Penyedia bisa mengizinkan yang satu dan membatasi yang lain. Credential yang sama bisa diterima server IMAP tapi ditolak server SMTP, atau sebaliknya. Menganggap "akun email" sebagai satu kesatuan yang jalan atau tidak jalan adalah penyederhanaan pertama yang gagal.

Kedua protokol juga punya dua cara enkripsi. Pada implicit TLS, koneksi sudah terenkripsi sejak byte pertama, lazimnya di port 465 untuk SMTP dan 993 untuk IMAP. Pada STARTTLS, koneksi dimulai sebagai teks polos lalu dinaikkan ke TLS sesudah salam pembuka, lazimnya di port 587 untuk SMTP dan 143 untuk IMAP. Banyak penyedia juga mewajibkan app password — password terpisah yang dibuat khusus untuk satu aplikasi — dan menolak password biasa tanpa penjelasan yang bisa ditindaklanjuti program.

B. Yang sebenarnya diandalkan aplikasi

Ketika aplikasi disebut "mengirim email", diam-diam aplikasi itu mengandalkan beberapa syarat sekaligus. Server SMTP harus terjangkau di port yang dikonfigurasi, dengan jenis enkripsi yang dikonfigurasi. Credential-nya harus diterima di sana. Server harus menerima alamat pengirim yang akan dipakai, dan alamat itu tidak selalu sama dengan nama login. Server juga harus menerima tiap penerima, bukan cuma sebagian.

Ketika aplikasi membaca email, daftarnya lain. Aplikasi harus bisa login ke server IMAP, membuka mailbox yang benar, dan melihat pesan di dalamnya. Kalau aplikasi bereaksi terhadap email baru, ia harus segera tahu ada pesan baru — idealnya lewat IMAP IDLE, extension yang membuat server bisa mendorong notifikasi begitu pesan tiba, bukan client yang terus-menerus bertanya. Dan semua itu harus selamat ketika koneksi putus, karena koneksi panjang ke mail server memang bisa putus.

Masing-masing adalah janji tersendiri, dan satu set credential bisa menepati sebagian sambil melanggar sebagian lain. Password yang jalan untuk IMAP tidak mengatakan apa-apa tentang apakah server SMTP akan menerima alamat pengirim tertentu.

C. Kenapa kegagalan email muncul jauh dari sebabnya

Waktu munculnya yang membuat kegagalan ini mahal. Pengiriman email biasanya termasuk fitur terakhir yang dipasang di aplikasi, dan sering jadi yang pertama gagal di tempat yang jauh dari kodenya. Kegagalannya tampil sebagai pendaftaran yang tak pernah mengirim konfirmasi, atau reset password yang tak pernah sampai, dan tersangka pertama yang wajar adalah aplikasinya: template, antrean, setelan mail di framework. Lebih dari sekali saya menghabiskan waktu men-debug kode aplikasi yang persoalan sebenarnya cuma credential, port, atau app password yang belum dibuat.

Sebagian kegagalan email juga senyap. Server bisa menerima pesan untuk satu penerima dan menolak penerima lain dalam transaksi yang sama, dan library pengirim tidak melempar error ketika itu terjadi. Pembaca mailbox yang membuka inbox dengan cara biasa mengubah status pesan yang dibacanya, jadi tindakan memeriksa ikut mengubah apa yang dilihat pemilik akun sesungguhnya. Tak satu pun dari keduanya kelihatan saat setup.

Tantangannya adalah membuktikan satu set credential email siap untuk kedua arah — mengirim dan menerima — sebelum aplikasi bergantung padanya, supaya tiap kegagalan sesudahnya bisa dilacak sumbernya dengan yakin. Dengan satu batasan: pemeriksaannya tidak boleh mengubah mailbox yang diperiksa, karena saya menjalankannya pada akun nyata yang dipakai orang.

D. Kenapa mail client biasa tidak menjawabnya

Ada cara biasa untuk memeriksa akun email. Mail client desktop bisa diarahkan ke akun itu. Perintah seperti openssl s_client bisa membuka koneksi lalu membiarkan saya mengetik perintah protokol secara manual. Keduanya berguna, dan keduanya menjawab pertanyaan yang lebih sempit daripada pertanyaan saya.

Mail client menyembunyikan persis detail yang nanti dihadapi aplikasi. Client menebak enkripsinya, mencoba ulang diam-diam, dan menandai pesan sebagai sudah dibaca begitu ditampilkan. Ketika berhasil, client membuktikan bahwa ada kombinasi setelan yang jalan, bukan bahwa kombinasi di file config saya yang jalan. Mengetik perintah protokol secara manual memang presisi, tapi cukup lambat sehingga akhirnya dilewati, dan tidak meninggalkan ringkasan apa pun. Yang saya inginkan: satu perintah, satu file config per akun, satu baris per check dengan hasil lolos atau gagal yang jelas, dan ringkasan di akhir yang menyatakan apakah akun itu siap dipakai.

Gampang dirumuskan, canggung dibangun. Desainnya akhirnya dibentuk oleh delapan hambatan, dikelompokkan menurut arah email.

E. Delapan hambatan yang menghalangi

Login yang berhasil tidak membuktikan email bisa dikirim. Server harus menerima pengirim dan tiap penerima, dan penolakan sebagian tidak melempar error. Menganggap login berhasil sebagai bukti adalah pilihan yang ditolak. Penggantinya: mengirim pesan sungguhan yang bisa dikenali, lalu memeriksa apa saja yang ditolak server.

Mode enkripsi harus cocok dengan port-nya. Implicit TLS di port STARTTLS, atau sebaliknya, berujung koneksi yang menggantung atau error handshake yang sama sekali tak menyebut port. Menebak mode dari nomor port adalah pilihan yang ditolak; mode dinyatakan di config, dan pesan error-nya menyebut kemungkinan salah pasang.

Kegagalan dari lapisan berbeda tampak serupa. Nama host yang tak bisa di-resolve, port yang diblokir, sertifikat yang buruk, dan password yang ditolak semuanya mengakhiri pemeriksaan dengan cara yang sama. Mencetak exception mentah adalah pilihan yang ditolak. Tiap golongan kegagalan diterjemahkan jadi satu baris yang menyebut lapisannya.

Inbox harus dibaca tanpa diubah. Membuka mailbox dengan cara biasa lalu mengambil pesan utuh membuat pesan-pesan itu ditandai sudah dibaca. Itulah pilihan yang ditolak; pemeriksaan membuka mailbox secara read-only dan cuma mengambil header.

Email baru harus terlihat begitu tiba. Menghitung pesan sekali cuma membuktikan mailbox bisa dibuka, bukan bahwa email baru bisa diketahui tepat waktu. Polling buta cuma cadangan, bukan bawaan. IMAP IDLE dipakai bila server mendukungnya, dan satu keanehan rentang nomor di protokolnya harus ditangani supaya email lama tidak tampil lagi.

Penantian harus selamat dari koneksi yang putus. Menunggu email dalam waktu lama pasti bertemu koneksi putus. Keluar pada putus pertama adalah pilihan yang ditolak. Pemeriksaan menyambung ulang dengan jeda yang makin panjang, lalu mengambil email yang tiba selama terputus.

Mengirim dan menerima harus disambungkan. Dua pengujian terpisah cuma membuktikan dua bagian terpisah. Ketika pesan uji dikirim ke akun yang sama dengan yang dibaca pemeriksaan, pesan itu semestinya muncul di tabel inbox — lingkaran dari ujung ke ujung tertutup.

Config yang rusak harus tampak berbeda dari server yang bermasalah. Field yang hilang atau alamat pengirim yang bukan alamat email adalah kesalahan saya, bukan penyedia. Mengembalikan satu kegagalan umum untuk keduanya adalah pilihan yang ditolak; config divalidasi lebih dulu, dan tiap jenis hasil punya exit code sendiri.

Urutannya mengikuti arah perjalanan email. Tiga yang pertama menyangkut jalur keluar, dan yang kedua serta ketiga berlaku untuk tiap koneksi yang dibuka. Tiga berikutnya menyangkut jalur masuk: membaca, menunggu, dan bertahan selama menunggu. Hambatan ketujuh menyambungkan kedua jalur, dan yang terakhir menarik garis di sekeliling semuanya, supaya kegagalan selalu jelas milik saya atau milik server, tak pernah mengambang di antaranya.

Metode

A. Membuktikan pesannya keluar, bukan cuma login-nya

Inilah jawaban atas hambatan pertama. Pemeriksaan login, lalu mengirim pesan sungguhan ke alamat yang diketik saat dijalankan. Subject pesan itu memuat token acak pendek, dan Message-ID-nya dibentuk dengan domain milik pengirim, sehingga pesan itu bisa dicari di inbox penerima dan dicocokkan dengan pasti ke satu kali jalan ini:

python
token = secrets.token_hex(4)
msg["Subject"] = f"[mail-tools] ... {now:%Y-%m-%d %H:%M:%S} #{token}"
msg["Message-ID"] = make_msgid(domain=cfg.from_address.split("@", 1)[1])
...
refused = server.send_message(msg)
if refused:
    reporter.check(..., False, f"... {refused}")

Dua baris terakhir paling penting. Ketika semua penerima ditolak, library melempar exception. Ketika cuma sebagian yang ditolak, library mengembalikan mereka dalam bentuk dictionary lalu melanjutkan seolah pengiriman berhasil. Pemeriksaan yang hanya menangkap exception akan melaporkan sukses untuk pesan yang tak pernah sampai ke sebagian penerimanya, jadi hanya dictionary kosong yang dihitung sebagai pengiriman berhasil.

Message-ID memakai domain pengirim karena bawaannya dibentuk dari hostname mesin lokal. Bawaan itu akan menaruh nama laptop saya di tiap pesan uji, dan tidak cocok dengan domain yang diklaim sebagai asal pesannya.

B. Mencocokkan enkripsi dengan port

Saya menolak membiarkan pemeriksaan menebak enkripsinya. Config menyebut mode enkripsinya secara eksplisit — tls untuk STARTTLS, ssl untuk implicit TLS, atau none — dan koneksi dibuka sesuai mode itu:

python
if cfg.encryption == "ssl":
    server = smtplib.SMTP_SSL(cfg.host, cfg.port, timeout=timeout, context=context)
else:
    server = smtplib.SMTP(cfg.host, cfg.port, timeout=timeout)
server.ehlo()
if cfg.encryption == "tls":
    server.starttls(context=context)
    server.ehlo()

Menyimpulkan mode dari port kelihatannya praktis, tapi port cuma kebiasaan, bukan aturan, dan server yang menjalankan STARTTLS di port tak lazim akan salah dinilai. Dengan mode yang dinyatakan, salah pasang gagal di langkah yang sudah diketahui. Ketika itu terjadi, pesannya meminta memeriksa kecocokan mode dengan port, karena error TLS di bawahnya tak pernah menyebutnya. Laporannya juga mencatat versi protokol yang disepakati, misalnya TLSv1.3, dan menampilkan peringatan jelas bila none dipilih, sebab login tanpa enkripsi hampir tak pernah disengaja.

C. Menyebut lapisan yang gagal

Saya membuat tiap kegagalan menyebut lapisannya sendiri. Tiap exception melewati satu fungsi yang mengubahnya jadi satu baris berisi lapisan tempat kegagalan terjadi:

python
if isinstance(exc, smtplib.SMTPAuthenticationError):
    return f"... ({exc.smtp_code}): ... App Password ..."
if isinstance(exc, ssl.SSLCertVerificationError):
    return f"...: {exc.verify_message}"
if isinstance(exc, socket.gaierror):
    return f"... (DNS): {exc}"
if isinstance(exc, ConnectionRefusedError):
    return "..."

Urutan pemeriksaannya penting karena golongannya tumpang-tindih: error sertifikat termasuk error SSL, dan kebanyakan error jaringan termasuk OSError. Kasus yang spesifik diletakkan lebih dulu, supaya kasus umum cuma menangkap sisanya. Kasus autentikasi menyebut app password secara eksplisit, karena itulah alasan paling umum password yang benar ditolak, dan balasan server sendiri jarang menyebutnya.

Di sinilah dulu sebagian besar waktu habis. Timeout, koneksi yang ditolak, dan host yang tak bisa di-resolve semuanya tampak seperti "tidak bisa tersambung", padahal masing-masing menunjuk perbaikan berbeda: firewall, port yang salah, atau salah ketik di hostname. Menyebut lapisannya mengubah satu sore penuh tebakan jadi satu baris bacaan.

D. Membaca tanpa meninggalkan jejak

Saya membuat pemeriksaan membaca inbox tanpa menyentuhnya. Sisi IMAP membuka mailbox secara read-only dan cuma meminta envelope serta waktu tiba tiap pesan:

python
info = client.select_folder(cfg.mailbox, readonly=True)
total = info.get(b"EXISTS", 0)
...
data = client.fetch(uids, ["INTERNALDATE", "ENVELOPE"])

Membuka mailbox secara read-only berarti tak ada flag pesan yang bisa berubah selama sesi. Mengambil envelope, bukan isi pesan, berarti server tak punya alasan menandai apa pun sebagai sudah dibaca. Tabel yang dihasilkan menampilkan nomor, timestamp, pengirim, dan subject untuk beberapa pesan terbaru — cukup untuk melihat bahwa mailbox yang benar terbuka dan berisi email, tanpa membaca email siapa pun dan tanpa mengubah apa yang dilihat pemilik akunnya sesudah itu.

Jumlah pesan diambil dari jawaban server saat mailbox dibuka, bukan dari daftar semua pesan. Pada mailbox berisi ribuan email, bedanya antara pemeriksaan seketika dan pemeriksaan yang lambat.

E. Menunggu email baru seperti aplikasi menunggunya

Saya ingin pemeriksaan menunggu email seperti aplikasi menunggunya. Sesudah tabel, pemeriksaan tetap tersambung dan menunggu email baru. Server ditanya apakah mendukung IDLE, dan IDLE dipakai bila didukung:

python
# RFC 2177: server boleh memutus IDLE sesudah 30 menit; perbarui jauh sebelumnya
IDLE_RENEW_SECONDS = 10 * 60
...
# "UID n:*" selalu memuat UID tertinggi walau lebih kecil dari n
uids = [u for u in client.search(["UID", f"{last_uid + 1}:*"]) if u > last_uid]

IDLE diperbarui tiap sepuluh menit, jauh di bawah tiga puluh menit yang diizinkan spesifikasinya sebelum server boleh menganggap client tidak aktif. Di dalam tiap periode IDLE, pemeriksaan bangun tiap tiga puluh detik untuk melihat apakah server mengumumkan pesan baru, lalu cuma mengambil pesan yang memang baru.

Baris kode kedua menangani keanehan yang gampang terlewat. Di IMAP, rentang n:* berarti "dari n sampai identifier tertinggi". Ketika tak ada pesan dengan identifier setinggi n, rentang itu dibaca terbalik dan tetap memuat pesan tertinggi. Meminta "semua sesudah pesan terakhir yang saya lihat" karenanya selalu mengembalikan setidaknya pesan terakhir itu, dan tanpa saringan tambahan, pesan lama yang paling baru akan dicetak ulang sesudah tiap penantian. Bila server tak punya IDLE, atau config mematikannya, pemeriksaan beralih ke polling dan menyebutnya di laporan, karena mailbox yang di-polling adalah janji yang lebih lambat daripada mailbox yang didorong server.

F. Bertahan ketika koneksi putus

Koneksi yang putus saya perlakukan sebagai kejadian biasa, bukan kegagalan fatal. Koneksi yang dibiarkan terbuka berjam-jam atau bermenit-menit cepat atau lambat akan terputus — oleh server, oleh router, oleh laptop yang masuk mode sleep. Loop penantian memperlakukannya sebagai kejadian biasa:

python
except (IMAPClientError, OSError) as exc:
    print(f"  [WARN] ... {describe_error(exc)} ... {backoff}s", flush=True)
    client = None
    time.sleep(backoff)
    backoff = min(backoff * 2, RECONNECT_MAX_BACKOFF)

Jedanya mulai lima detik dan berlipat dua tiap kali gagal sampai satu menit, jadi gangguan singkat tak banyak memakan waktu dan gangguan panjang tak membanjiri server. Saat tersambung kembali, loop login ulang, membuka mailbox read-only lagi, lalu mengambil semua pesan sesudah identifier terakhir yang dilihatnya. Pesan yang tiba selama koneksi putus tetap muncul, berurutan, di tabel yang sama.

Bagian ini juga latihan kecil untuk yang nanti harus dikerjakan aplikasinya sendiri. Kalau pemeriksaan butuh logika reconnect supaya tetap berguna selama satu sore, begitu pula program apa pun yang memantau mailbox yang sama di production.

G. Menutup lingkaran dari mengirim ke menerima

Saya menyatukan kedua arah dalam satu jalan. Pemeriksaan bisa menjalankan SMTP saja, IMAP saja, atau keduanya berurutan. Pada mode gabungan, ketika pesan uji dialamatkan ke akun yang sama dengan yang dibaca sisi IMAP, laporannya menyebut hal itu dan pesannya semestinya muncul di tabel inbox beberapa detik kemudian. Satu contoh jalan yang dipersingkat, dengan akun yang digenerikkan:

bash
=== SMTP ===
  [ OK ] Koneksi ke server — smtp.example.com:587
  [ OK ] STARTTLS — TLSv1.3
  [ OK ] Login — user@example.com
  [ OK ] Kirim email uji — ke user@example.com, token #a1b2c3d4
=== IMAP ===
  [ OK ] Dukungan IMAP IDLE — email baru tampil real-time
     6 | 2026-09-26 12:40:43 | user@example.com | [mail-tools] ... #a1b2c3d4

Baris terakhir itu bukti terkuat yang bisa dihasilkan pemeriksaan. Baris itu menunjukkan pesan keluar lewat SMTP, dikirim oleh penyedia, lalu tiba lewat IMAP, dengan token yang sama di kedua ujung. Dua pengujian terpisah bisa sama-sama lolos padahal jalur di antaranya rusak — misalnya pesan yang diterima untuk dikirim lalu tersaring di tengah jalan. Jalan gabungan tidak menyisakan celah itu untuk akun ini.

H. Membedakan config yang rusak dari server yang bermasalah

Saya membuat kesalahan saya sendiri tampak berbeda dari kesalahan server. File config divalidasi seluruhnya sebelum koneksi apa pun dibuka, dan persoalan di dalamnya mengakhiri jalan dengan exit code sendiri:

python
CONFIG_ERROR = 2
CANCELLED = 130
...
from_address = _as_str(section, "from_address", "smtp", default=username)
if not is_email(from_address):
    raise ConfigError(...)

Alamat pengirim bawaannya sama dengan username, karena pada kebanyakan penyedia keduanya memang sama. Pada sebagian penyedia tidak — nama login tanpa domain, atau akun bersama yang mengirim atas nama alamat lain — dan pengirim yang bukan alamat email akan gagal jauh lebih belakangan, di server, dan tampak seperti kesalahan server. Port wajib berupa bilangan bulat dalam rentang yang sah, nilai boolean wajib benar-benar boolean, dan mode enkripsi wajib salah satu dari tiga nilai yang dikenal.

Exit code membawa pembedaan itu ke program apa pun yang menjalankan pemeriksaan: 0 bila semua check lolos, 1 bila ada check yang gagal di server, 2 bila config-nya sendiri salah, dan 130 bila saya membatalkan prompt. Script bisa membedakan "perbaiki file saya" dari "akunnya belum siap" tanpa mengurai output.

Hasil dan Pembahasan

A. Yang sebenarnya dibuktikan jalan yang lolos

Jalan yang lolos berakhir dengan ringkasan per protokol — misalnya empat dari empat check di sisi SMTP — dan status akhir bahwa akun siap dipakai. Di balik baris itu ada fakta-fakta spesifik: server terjangkau di port yang dikonfigurasi, enkripsi yang dikonfigurasi berhasil disepakati, credential diterima, pengirim dan tiap penerima diterima, mailbox terbuka dengan jumlah pesan yang diharapkan, email baru bisa didorong atau setidaknya di-polling, dan pada mode gabungan, satu pesan keluar lalu kembali masuk.

KeputusanYang didapatAlternatif yang ditolak
kirim pesan bertoken dan periksa penolakanbukti pengiriman, bukan cuma loginmenganggap login berhasil sudah cukup
mode enkripsi eksplisitsalah pasang gagal di langkah yang diketahuimenyimpulkan mode dari port
satu baris per lapisan kegagalanperbaikannya terbaca dari pesannyamencetak exception mentah
mailbox read-only, header sajainbox tertinggal persis seperti semulamembuka biasa dan mengambil isi pesan
IDLE dengan pembaruan dan saringan UIDemail baru tampil sekali, begitu tibapolling buta sebagai bawaan
reconnect dengan jeda bertambahpenantian selamat dari koneksi putuskeluar pada putus pertama
jalan gabungan kirim dan terimabukti di sepanjang jalurdua pengujian yang tak berhubungan
config divalidasi dulu, exit code terpisahkesalahan saya tak pernah tampak seperti kesalahan serversatu kode kegagalan umum

B. Kegagalan yang tidak berupa error

Dua dari delapan hambatan menyangkut kegagalan yang sama sekali tidak tampak seperti kegagalan. Pengiriman yang ditolak sebagian kembali dengan normal. Pemeriksaan inbox yang menandai pesan sebagai sudah dibaca bekerja sempurna sambil diam-diam mengubah mailbox. Tak satu pun menghasilkan pesan error, dan keduanya akan lolos dari pemeriksaan yang cuma ditulis untuk menangkap exception.

Karena itulah seluruh pemeriksaan ditulis dengan cara tertentu. Tiap langkah mencatat lolos atau gagal secara eksplisit beserta alasannya, bukan mengandalkan ketiadaan exception. Ringkasannya menghitung check, bukan melaporkan "tidak ada error", dan mencantumkan tiap check yang gagal beserta rinciannya. Jalan yang berbunyi empat dari empat sudah mengajukan empat pertanyaan spesifik dan mendengar empat jawaban spesifik.

C. Batas kegunaan tool ini

Jalan yang lolos membuktikan akun itu jalan dari mesin saya, pada saat pemeriksaan dijalankan. Jalan itu tidak membuktikan host aplikasi bisa menjangkau port yang sama; jaringan production bisa memblokir SMTP keluar yang dari laptop terjangkau bebas. Deliverability juga tidak diukur: pesan yang tiba di inbox uji saya tetap bisa masuk folder spam di tempat lain, dan itu bergantung pada record domain pengirim, bukan pada credential-nya.

Batas pengiriman juga tidak diuji. Penyedia membatasi berapa banyak pesan yang boleh dikirim satu akun per hari atau per menit, dan satu pesan uji tak mengatakan apa-apa tentang letak batas itu. Aplikasi yang mengirim dalam jumlah besar butuh pengujian jenis lain, dijalankan dengan volume pengiriman yang sesungguhnya.

Simpulan

Credential email gagal belakangan dan jauh dari sebabnya: di reset password yang tak pernah sampai, di balasan yang tak pernah diketahui. Pemeriksaan yang dibahas di sini membuktikan kedua arah sebelum ada kode aplikasi yang bergantung padanya. Pemeriksaan ini mengirim pesan sungguhan yang bisa dikenali dan memeriksa apa saja yang ditolak server. Mode enkripsinya dinyatakan, bukan ditebak, dan lapisan tiap kegagalan disebut namanya. Inbox dibaca tanpa diubah, email baru ditunggu seperti aplikasi menunggunya, koneksi yang putus disambung lagi, dan kedua arah disatukan lewat satu pesan yang terlihat di kedua ujung. Di sekeliling semua itu, kesalahan config dipisahkan dari persoalan server.

Pelajaran yang terbawa adalah gagasan di balik tiap keputusan itu: buktikan kesiapan dengan menjalankan tiap janji yang akan diandalkan aplikasi, satu per satu, dan laporkan tiap hasilnya secara eksplisit, supaya kegagalan di kemudian hari bisa dilacak ke aplikasinya dan bukan ke akun di bawahnya.

Istilah

SMTP
Protokol untuk menyerahkan pesan ke mail server agar dikirim. Aplikasi memakainya untuk mengirim email.
IMAP
Protokol untuk membaca mailbox di server. Aplikasi memakainya untuk menemukan dan menanggapi email yang masuk.
STARTTLS
Koneksi yang dimulai sebagai teks polos lalu dinaikkan ke TLS sesudah salam pembuka, lazimnya di port 587 untuk SMTP dan 143 untuk IMAP.
implicit TLS
Koneksi yang terenkripsi sejak byte pertama, lazimnya di port 465 untuk SMTP dan 993 untuk IMAP.
app password
Password terpisah yang dibuat untuk satu aplikasi, yang diwajibkan sebagian penyedia sebagai pengganti password akun biasa.
IMAP IDLE
Extension IMAP yang memungkinkan server memberi tahu client yang tersambung begitu email baru tiba, tanpa client harus bertanya berulang kali.
UID
Nomor yang diberikan server IMAP kepada tiap pesan di mailbox. Nomornya cuma bertambah, jadi bisa dipakai untuk meminta semua pesan sesudah pesan tertentu.
Message-ID
Header yang memberi tiap email identifier unik global, dipakai sistem email untuk melacak dan mengelompokkan percakapan.
exit code
Angka yang dikembalikan program ketika berakhir. Script memakainya untuk membedakan sukses dari berbagai jenis kegagalan tanpa membaca output.

Technologies covered

  • Python
  • IMAP
  • SMTP
  • TLS

About the author

Photo of Romi Fadli

Romi Fadli, S.Kom.

Solution Architect · Backend Engineer · Embedded Systems

In my day job I work as a solution architect. I design the system, lead the team that builds it — quality assurance, backend developers, a UI developer, DevOps and DevSecOps — and write the most critical modules myself, such as a certificate authority, a long-term PDF signer, and the core API of a personnel system.

I work across the whole stack that a running system actually needs. On the hardware side that means PCB design, microcontrollers, and embedded firmware — including RISC-V boards running FreeRTOS and Embassy, speaking MQTT over mutually authenticated TLS on a microcontroller with 64 KB of SRAM.

Most of my day-to-day work is software: Rust backends and API modules built on Clean Architecture, an OAuth2/OpenID Connect authorization server written from scratch, PAdES document signing, and certificate authorities — root CAs, timestamping authorities, and OCSP responders. I also build the frontends that sit on top of them.

Running all of that means infrastructure: Docker Compose stacks, networking, Mikrotik and RADIUS, observability with OpenTelemetry, and a steady habit of writing command-line tools and utilities for Linux when the existing ones do not fit.

Share

Enjoyed this?

Get an email when a new article is published.

Related Articles

A checklist card on the left, a three-part token in the middle with a check mark, and an identity provider marked with a key on the right

Script Python interaktif yang membuktikan identity provider siap untuk satu client: ia menggladi login dan logout PKCE utuh seperti aplikasi, lalu memeriksa token yang diterimanya terhadap key milik provider sebagaimana yang wajib dilakukan aplikasi.

11 min read Read more →