You are browsing as a guest. Sign up (or log in) to start making projects!

Amelu, Managed Email for Your Domain

  • 7 Devlogs
  • 20 Total hours

Amelu is a managed email hosting platform for custom domains. Users can connect and verify their domain, configure DNS, create mailboxes and aliases, set up catch-all forwarding, and access their email through any standard IMAP, SMTP, or POP3 client.

Open comments for this post

47m 41s logged

Reported as “login screen isn’t always immediately visible.” The actual
bug was upstream of LoginPage entirely. Every real route, including “/”,
sits behind ProtectedRoute (App.tsx), and ProtectedRoute returned null
while AuthContext’s api.me() call was still pending on first load.

Most visits land on “/”, not “/login” directly, so the sequence was:
render nothing -> wait for /api/me -> get 401 -> Navigate to /login ->
LoginPage finally renders. The blank stretch is proportional to how long
/api/me takes (cold backend, slow network), which is why it only showed
up “sometimes” - a fast response made the gap invisible.

Fixed ProtectedRoute.tsx to render the same “Loading…” text used by
AcceptInvitePage instead of null while loading is true.

Not touched: LoginPage.module.css’s 0.4s fadeIn on .loginCard. That’s a
deliberate entrance animation, not a bug - once the blank-screen race
above is closed, it just reads as a normal fade-in.

0
0
8
Open comments for this post

26m 4s logged

/account/api-keys is a real page now. Create a key with a name, see prefix,
created and last used, revoke it. The raw key is shown once in the response
and never stored - api_keys (migration 0025) holds a SHA-256 hash, the same
shape as sessions and organization_invitations. prefix is amelu_live_ plus 6
of the 43 random characters, purely so two keys can be told apart on screen.

Keys authenticate through auth.Require, which now takes either the session
cookie or Authorization: Bearer amelu_live_…, so every /api route accepted
them the moment the middleware changed - no per-route work. A key acts as its
owning customer with that customer’s organization role.

0
0
3
Open comments for this post

38m 41s logged

New page at /account/appearance (AccountAppearancePage, sidebar entry between
Password and API Keys) with a three way segmented control: Light, Dark,
System. The choice is stored in localStorage under amelu.theme, so it is per
device rather than on the customer record, and applies immediately without a
Save button.

app.css was already almost entirely token driven, so dark is one extra block:
:root[data-theme=“dark”] restating –bg/–surface/–text/–border/–link plus
a few –md-sys-color-* tokens. data-theme is only ever “light” or “dark” -
“system” is resolved against prefers-color-scheme in ThemeContext and stamped
on , which keeps duplicated media queries out of the stylesheet. An
inline script in index.html stamps it before first paint, otherwise dark users
got a flash of the light palette on every load.

Four colours were hardcoded and had to become tokens: main’s background
(#0043ce, the filler behind .container’s 28px border-top-left-radius) showed a
bright blue wedge against the dark page, and md-filled-button plus
.segmented-option-active/.button-pill had color: #ffffff, which is unreadable
on dark’s lighter –link. Chrome blue is now –chrome-bg/–chrome-surface,
navy #0a1f4a in dark. The select arrow SVG was #e8eaed, invisible on white -
it was a leftover from an earlier dark theme, so it had been broken in light
all along.

0
0
3
Open comments for this post

18m 18s logged

A reviewer said it was disappointing that you couldn’t self-host Amelu even
for your own purpose. Fair point, so the license is now 1.1 with a Personal
Use carve-out: run your own instance for free, for your own mailboxes and
domains and your household’s, modify it, keep a fork.

What still needs a separate license is the commercial half. Running it for a
company or team, charging for access, or hosting mailboxes for anyone outside
your household. The line is “am I the only one benefiting”, not “is money
involved”.

README and CONTRIBUTING both still said self-hosting was forbidden, so those
moved with it. Self-hosting is unsupported though, you run your own Stalwart
and your own Postgres.

0
0
56
Open comments for this post

2h 32m 1s logged

spent this day mostly on the dashboard and a couple of backend correctness bugs that had been quietly annoying me.

biggest visible change: full re-theme of the app. it was still running the old dark theme from way earlier and never really matched the brand, so i redid it, brand blue topbar and sidebar, white logo, white content area, unified every single button in the app (there were like three different ad-hoc button styles floating around) to match the material “add a domain” style. sounds cosmetic but it’s the difference between something that looks like a demo and something that looks like a product.

also split dns configuration into two pages instead of one giant table. now there’s an overview that just tells you at a glance what’s verified, what’s mismatched, what’s missing, with a one-click fix-via-cloudflare action, and a separate config page for people who actually want to see the raw records. most people just want the overview.

fixed a genuinely annoying bug where creating a domain that already existed in stalwart (retry after a partial failure, whatever) threw a hard error instead of just adopting the existing record. now it checks for that specific failure mode and recovers instead of yelling at the user.

also fixed the mailbox creation flow so it always shows you the password you ended up with, whether you typed it or generated it. previously it only showed you the password if it was auto-generated, so if you typed your own you had zero confirmation it actually saved what you thought it saved. small thing, but it was a real source of confusion.

backend housekeeping: dropped a decommissioned mail node from the backup mx config (down to one backup node now, cluster’s simpler), and spent a chunk of time untangling some repo state that had gotten gnarly, stray worktree metadata that broke git status for the whole repo, plus reconciling a branch that had diverged after some readme edits landed upstream while i was working. not fun work, but the kind of thing that turns into a bigger mess the longer you leave it.

feels like the product surface is converging. onward.

0
0
14
Ship #1

worked on polishing amelu this week. focused mostly on improving reliability, cleaning up the dashboard, fixing bugs, and making the overall experience smoother from onboarding to mailbox management.

it’s starting to feel like a real product instead of just infrastructure glued together. if you’re trying it out, i’d love feedback on the onboarding flow and anything that feels confusing or rough around the edges.

  • 2 devlogs
  • 15h
  • 16.47x multiplier
  • 179 Stardust
Try project → See source code →
Open comments for this post

3h 0m 52s logged

been heads down on amelu again this week and it finally feels like a lot of the rough edges are getting sanded off.most of the work has been around making the actual management experience better instead of just getting features to exist. cleaned up a bunch of backend flows, simplified some of the api surface, and spent way too long chasing down all the little edge cases that only show up once you start using the product like a real customer instead of a developer. those are always the most annoying bugs because everything mostly works until it very much doesn’t.also spent some time tightening up the provisioning flow. there’s a lot of moving pieces between the dashboard, the mail server, dns, billing and background jobs, so a big focus was making those operations more reliable and idempotent. less “hope this finishes” and more “run it again and it still ends up in the right state.” not the kind of work anyone notices, but definitely the kind of work that keeps support tickets from existing.frontend got some love too. went through a bunch of the dashboard and polished the smaller interactions that had been bothering me for a while. loading states, empty states, navigation, forms, the usual death-by-a-thousand-paper-cuts stuff. it’s funny how a dozen tiny improvements end up making the whole app feel way more finished than one giant feature ever does.did a bit of housekeeping as well. cleaned up old code that survived longer than it should have, removed some duplicated logic, and generally tried to leave the codebase in a better state than i found it. always satisfying when deleting code feels like progress.still nowhere near “done” (i don’t think software ever is), but the project is definitely shifting from building the foundation to refining the experience. feels less like assembling infrastructure now and more like building something people could actually rely on every day.onward.

0
0
20
Open comments for this post

12h 10m 23s logged

ok so this week was a big one. been building amelu, which is basically an email hosting thing, self-hosted mail server (stalwart) under the hood but with a normal dashboard on top so people don’t have to touch config files to get a working inbox on their own domain.

spent a good chunk of time on the backend. it’s go, talks to postgres directly (no orm, just raw sql, which honestly i like more than i expected). moved a bunch of stuff around cloudflare’s stack too, pages for the frontend, a worker sitting in front of the api, tunnel connecting into the actual server since i didn’t want to expose postgres or the mail server to the open internet. found and fixed a nasty bug where the worker was silently eating oauth redirects lol, that one took a minute to track down.

also wired up stripe billing properly, rotated us off the test key onto live, which broke checkou

biggest thing though: built a whole admin surface so we (ordnary staff) can actually manage amelu customers from inside helm, which is our internal company dashboard. domains, mailboxes, subscriptions, all of it, without needing to ssh into anything or run raw sql against prod. it’s hmac signed between the two services so every action gets attributed to whoever did it. also added a little workspace switcher in the sidebar so you can flip between “ordnary helm” and “ordnary amelu” like switching tabs.

on top of that, built the actual marketing site from scratch. real screenshots, real pricing, wrote a whole custom license since amelu is source available but not like, fork-it-and-compete-with-us open source. also did the seo stuff properly, og images, structured data, all that.

still a lot left to do but it feels like a real product now instead of a pile of scripts. onward.

0
0
15

Delete project?

Are you sure you want to permanently delete this project? This action cannot be undone.

All devlogs, followers, and associated data will be removed.

Followers

Loading…