I built a detention-pay calculator for truckers in a day — unglamourous niches beat another AI wrapper
I built a detention-pay calculator for truckers in a day — unglamourous niches beat another AI wrapper
我花了一天时间为卡车司机开发了一个滞留费计算器——比起又一个 AI 套壳应用,这种“不性感”的利基市场更有价值
Every “what should I build” thread on here is full of AI wrappers fighting over the same five SaaS founders. Meanwhile there’s a guy sitting at a loading dock right now, doing arithmetic in his head, who is about to undercharge his broker by a few hundred bucks because nobody built him a 30-second tool. I built that tool. It’s a free detention-pay calculator for truck drivers. This is the build log — the niche-selection, the single-file stack, and two decisions (an SVG gauge and a no-mail-service auth scheme) that were more interesting than the app deserves. I’m not a trucker. I build small free web tools for industries other may find unglamourous or not enticing enough. That honesty matters later.
在各种“我该开发什么”的讨论帖中,到处都是争抢那五类 SaaS 创始人的 AI 套壳应用。与此同时,某个卡车司机正坐在装卸码头,脑子里做着算术,却因为没人给他提供一个 30 秒就能搞定的工具,即将少收货运经纪人几百美元。我开发了这个工具。这是一个为卡车司机提供的免费滞留费计算器。这是我的开发日志——包括利基市场的选择、单文件技术栈,以及两个比应用本身更有趣的决策(SVG 仪表盘和无需邮件服务的身份验证方案)。我不是卡车司机,我只是为那些别人觉得“不性感”或不够吸引人的行业开发小型免费 Web 工具。这种坦诚在后面会很重要。
The problem (worth $2–6k/yr to one user)
问题所在(对单个用户而言每年价值 2000–6000 美元)
Truckers get a “free time” window at a dock — usually 2 hours. Past that, the broker owes detention pay (~$50–100/hr). Drivers leave an estimated $2,000–6,000/year of it unclaimed, mostly because the math + the paperwork is annoying enough to skip. So the spec wrote itself: In/out times + free hours + rate → dollars owed. Export a dispute-ready PDF they can email the broker. Work on a phone, no login, instant.
卡车司机在码头通常有 2 小时的“免费等待时间”。超过这个时间,货运经纪人就必须支付滞留费(约 50–100 美元/小时)。据估计,司机每年有 2000 到 6000 美元的滞留费未领取,主要是因为计算和填表太麻烦,他们干脆放弃了。因此,需求规格不言自明:进出时间 + 免费时长 + 费率 → 应付金额。导出一个可用于争议处理的 PDF,方便他们发给经纪人。要求:手机端可用、无需登录、即开即用。
Validating before writing a line
在写代码前进行验证
The mistake I almost made: assume the niche is empty because I’d never heard of it. I checked. It is not empty — DockClaim ($49/mo, GPS tracking), Detention Buddy, a couple of $9.99/mo App Store apps, even a free email-gated web calculator or two. That killed my first instinct (“be the only one”) but clarified the real wedge: everything is a paid app download or email-gated. The opening was a genuinely free, no-signup, instant web version that also generates the claim PDF. Not “the only detention tool” — the one with the least friction. I’ll say more on why I’m careful about that claim at the end. Lesson: validate to find your angle, not just a go/no-go. “Crowded but all friction-heavy” is a fine market.
我差点犯的错误是:因为没听说过这个领域,就以为它是空白的。我查了一下,发现并非如此——已有 DockClaim(每月 49 美元,带 GPS 追踪)、Detention Buddy、几个每月 9.99 美元的 App Store 应用,甚至还有一两个需要输入邮箱才能使用的免费网页计算器。这打消了我最初“做唯一一个”的念头,但明确了真正的切入点:现有的产品要么需要付费下载,要么需要注册邮箱。我的机会在于:一个真正免费、无需注册、即时响应且能生成 PDF 的网页版。它不是“唯一的滞留费工具”,而是“摩擦力最小”的那个。关于为什么我对此说法保持谨慎,我会在最后说明。经验教训:验证是为了找到你的切入角度,而不仅仅是决定做还是不做。“竞争激烈但摩擦力大”其实是一个很好的市场。
The stack: one HTML file
技术栈:单个 HTML 文件
No framework. The whole app is a single self-contained .html — markup, CSS, vanilla JS, jsPDF from a CDN. It deploys as a static asset. Cold-loads instantly on a trucker’s phone on dock wifi, which is the only performance budget that matters here. The core math is anticlimactic, which is the point — including the one edge case people forget, the overnight dock wait:
没有框架。整个应用就是一个自包含的 .html 文件——包含标记语言、CSS、原生 JS 以及通过 CDN 引入的 jsPDF。它作为静态资源部署。在码头的 Wi-Fi 环境下,它能在卡车司机的手机上瞬间加载,这是这里唯一重要的性能指标。核心算法非常简单,这正是重点所在——包括人们容易忽略的一个边缘情况:跨夜等待:
function detentionOwed(arriveMin, leaveMin, freeHrs, rate) {
let wait = leaveMin - arriveMin;
if (wait < 0) wait += 24 * 60; // crossed midnight at the dock
const billable = Math.max(0, wait / 60 - freeHrs);
const charged = Math.ceil(billable); // brokers bill by the hour
return charged * rate;
}
The PDF is the actual product. jsPDF, a few doc.text() calls laying out a claim, and a footer line — “Generated free at quackbuilds.com…”. That watermark is the entire distribution strategy: every claim a driver emails a broker carries the tool into a logistics company’s inbox. The artifact does the marketing.
PDF 才是真正的产品。通过 jsPDF 和几个 doc.text() 调用来排版索赔单,并在页脚加上一行字——“由 quackbuilds.com 免费生成……”。这个水印就是整个分发策略:司机发给经纪人的每一份索赔单,都将这个工具带入了物流公司的收件箱。产品本身就是营销。
Decision 1: an SVG instrument gauge instead of a progress bar
决策 1:使用 SVG 仪表盘而非进度条
The audience reads dashboards all day, so the result is a round gauge — a dim “free-clock” arc that hands off to a glowing amber “billable” arc, with a seven-segment LED readout (DSEG font) in the center. The trick is splitting one dial into two arcs with stroke-dasharray + stroke-dashoffset. A 270° sweep, the free segment first, the billable segment offset to start exactly where free ends.
目标用户整天都在看仪表盘,所以结果是一个圆形的仪表盘——一个暗淡的“免费时间”弧线过渡到一个发光的琥珀色“计费”弧线,中心是一个七段式 LED 显示屏(DSEG 字体)。诀窍在于利用 stroke-dasharray 和 stroke-dashoffset 将一个圆环拆分为两个弧线。270 度的扫描范围,先是免费部分,然后是计费部分,通过偏移量让计费部分正好从免费部分结束的地方开始。
Decision 2: cross-device sync with no email service
决策 2:无需邮件服务的跨设备同步
Users wanted their saved loads to survive across phone → laptop. That needs identity. The repo’s other apps use crypto wallet connect — great, except truckers are roughly the least crypto-native audience alive. And there was no mail service wired up, so magic-links were off the table without adding (and paying for) one. So: email + a recovery code. On first backup the server mints a high-entropy code, returns it once, and stores only an HMAC of it. Restore = email + code. Cheap, no mail provider, and the code is the secret so it’s not guessable like a bare email would be.
用户希望保存的装载记录能在手机和笔记本电脑之间同步。这需要身份验证。我仓库里的其他应用使用加密钱包连接——这很好,但卡车司机几乎是世界上最不了解加密货币的群体。而且我没有接入邮件服务,所以如果不额外增加(并付费)邮件服务,魔法链接(magic-links)方案就不可行。因此:采用“邮箱 + 恢复码”方案。首次备份时,服务器生成一个高熵代码,返回给用户一次,并仅存储其 HMAC 值。恢复时只需输入邮箱和代码。成本低,无需邮件服务,且代码作为密钥,比单纯的邮箱更难被猜到。
The gotcha that almost shipped
差点酿成大祸的疏忽
The repo’s shared Supabase client uses the anon key. Convenient — and it means any table that client touches is reachable through public PostgREST. Fine for public data; not fine for a table of user logs. Someone could enumerate rows with the anon key that ships in the browser bundle. Fix was two lines: route the API through the service-role client, and in the migration alter table … enable row level security; with no policies. No policies = anon/authenticated denied entirely; the service role bypasses RLS. The table becomes reachable only through the server route that mediates the code check. Easy to forget, easy to verify — I had the API smoke-tested for bad_code, not_found, and the happy path before trusting it.
仓库中共享的 Supabase 客户端使用了匿名密钥(anon key)。这很方便,但也意味着该客户端接触到的任何表都可以通过公共 PostgREST 访问。这对公共数据没问题,但对用户日志表来说就不行了。有人可能会利用浏览器包中携带的匿名密钥枚举行数据。修复方法只需两行:将 API 路由通过 service-role 客户端处理,并在迁移脚本中执行 alter table ... enable row level security,且不设置任何策略。没有策略意味着匿名/已认证用户完全被拒绝访问;而 service-role 可以绕过 RLS。这样,表只能通过处理代码校验的服务器路由来访问。这很容易被遗忘,但也容易验证——我在信任它之前,对 API 进行了冒烟测试,涵盖了错误代码、未找到和正常路径。
Why “unglamourous” is the moat
为什么“不性感”是护城河
Three things fall out of picking an unglamorous niche: The spec is knowable. I’m not guessing what a “better AI assistant” means. Detention pay has rules. You build to them and you’re done. Distribution is built in. The PDF goes where the buyers are (brokers/logistics ops) without me running ads to them.
选择一个“不性感”的利基市场有三个好处:需求规格是明确的。我不需要去猜测“更好的 AI 助手”意味着什么。滞留费有明确的规则,按规则开发完就大功告成了。分发是内置的。PDF 会直接到达买家(经纪人/物流运营)手中,我根本不需要为他们投放广告。