formgate: A Contact Form Gateway, Part 2: The Pipeline

Part of the formgate series. What formgate is around 1300 lines of Go across nine files, nothing outside the standard library. Part 1 covered the design decisions: why the emailed OTP had to go, why the sender and recipient come only from config, and how each pipeline step picks a failure direction. This part is the implementation. The whole pipeline lives in one HTTP handler, and the failure directions from Part 1 show up as concrete branches inside it. ...

September 6, 2026 · 6 min · 1201 words · Sagar Nayak

formgate: A Contact Form Gateway, Part 1: Bots and Relays

What formgate is a single Go binary, nothing outside the standard library, one config file. It sits between static marketing sites and SMTP. A public contact form posts JSON to it; formgate decides whether the submission is worth an email, and if it is, delivers it. The mail path is never exposed to the page. It serves twelve sites right now. Why The OTP that amplified spam Static sites still need contact forms, and contact forms need mail. The question is how the form sends mail without putting SMTP credentials inside a public page. ...

August 30, 2026 · 5 min · 914 words · Sagar Nayak

Running IMAP Migration Jobs in PurelyManage

Part of the PurelyManage series. The migration system in PurelyManage wraps imapsync and adds a job queue, a UI, and a few quality-of-life features on top. This post covers the job system from the user side: how jobs are created, run, monitored, and managed. The imapsync installation and the async job architecture under the hood are covered in the next post. Server Configuration Before running any migration you need to add IMAP server definitions. A server definition stores only the connection details: host, port, and whether to use SSL. Credentials are entered per-job, not per-server, so you can reuse the same server config across many migrations without re-entering connection details each time. ...

January 14, 2026 · 5 min · 944 words · Sagar Nayak

Collecting IMAP Credentials Without Storing Plaintext

Part of the PurelyManage series. The Problem Migrating an organization’s email means moving every mailbox from the old provider into PurelyMail. Each mailbox requires the source IMAP credentials: the email address and password the user logs in with on the old system. The naive way to collect these is to email everyone a spreadsheet and ask them to fill in their passwords. That spreadsheet then sits in someone’s inbox or a shared drive, plaintext, accessible to anyone who can read it. It is also error-prone: users mistype passwords, the admin has no way to know if a credential is correct until the migration job fails. ...

January 8, 2026 · 5 min · 1017 words · Sagar Nayak

Email User Management in PurelyManage

Part of the PurelyManage series. Listing Users The Users page shows all email accounts across all your domains. The list is served from a local PostgreSQL cache rather than a live PurelyMail API call on every page load. The reason is speed and rate limits. PurelyMail’s listUser endpoint returns all users in one call, but calling it on every page load when you have 50+ users and the page auto-refreshes every 60 seconds is wasteful. The cache (purelymail_users table) is populated on first boot and kept in sync by a background cron that runs a full refresh daily at 2am. When a user is created or deleted through the panel, the cache is updated immediately so the UI reflects the change without waiting for the cron. ...

December 27, 2025 · 4 min · 678 words · Sagar Nayak