Skip to main content

max / makenotwork

Fix custom-domain routing instructions; document stable connect target The setup guide told creators to CNAME their domain to makenot.work, but that resolves to Cloudflare's edge, not the origin where on-demand TLS (LE HTTP-01) issues. A creator following it got a verified-but-broken domain: wrong target, no certificate. The dashboard compounded it by showing only the TXT verify record, so verifying looked like completion. Route creators to a stable, origin-direct target instead: - guide/custom-domains.md: CNAME -> connect.makenot.work, "DNS only / proxy off" callout, apex flattening note, explicit two-step framing. - dashboard user_profile.html: show both records as step 1 (point) + step 2 (verify) instead of TXT only. - api/domains.rs, cli_features.rs, dashboard user tab: instruction strings made consistent (routing + verify). - scaling.md: document connect.makenot.work (A -> origin, proxy off) as the routing target; note maxj.phd now dogfoods the direct custom-domain path rather than being a CF-proxied zone. Operator follow-up (DNS + cert) tracked in the launch plan. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Author: Max Johnson <me@maxj.phd> · 2026-06-08 20:38 UTC
Commit: 0b2197ca6c2a3b998ad6233c3f2e0180803ef029
Parent: 86b9a09
6 files changed, +44 insertions, -21 deletions
@@ -8,7 +8,9 @@ Capacity assessment of the production stack and the upgrade path from current st
8 8
9 9 CCX13 = 2 dedicated vCPU / 8 GB RAM / 80 GB NVMe + 10 GB volume, ~20 TB included monthly egress.
10 10
11 - **Edge** — Cloudflare proxy ON for `makenot.work`, `*.makenot.work`, `maxj.phd`, `*.maxj.phd`, `htpy.app`. Full (Strict) SSL via Origin CA wildcards. Authenticated Origin Pulls (mTLS) — origin only accepts the Cloudflare client cert. `cdn.makenot.work` reverse-proxies to Hetzner Object Storage (`fsn1`), with Cloudflare caching at the edge. Custom-domain fans use Caddy on-demand TLS (LE HTTP-01) and bypass Cloudflare.
11 + **Edge** — Cloudflare proxy ON for `makenot.work`, `*.makenot.work`, `*.maxj.phd` (e.g. `dl.maxj.phd`), `htpy.app`. Full (Strict) SSL via Origin CA wildcards. Authenticated Origin Pulls (mTLS) — origin only accepts the Cloudflare client cert. `cdn.makenot.work` reverse-proxies to Hetzner Object Storage (`fsn1`), with Cloudflare caching at the edge.
12 +
13 + **Custom domains** — creator domains bypass Cloudflare and point at the origin directly, where Caddy issues on-demand TLS (LE HTTP-01, gated by `/api/domains/caddy-ask`). Stable routing target: `connect.makenot.work` (A → origin IP, **proxy OFF**) — customers CNAME to it (apex via flattening) so the origin IP can change in one place. **The apex `maxj.phd` dogfoods this exact path**: it is a verified custom domain, DNS-only (CNAME-flattened → `connect.makenot.work`), served by the on-demand-LE catch-all — *not* a CF-proxied zone. (`dl.maxj.phd` stays CF-proxied with its own Origin CA cert + mTLS.)
12 14
13 15 **Object storage** — Hetzner S3 (`fsn1` Frankfurt), presigned PUT/GET. Separate buckets for content and SyncKit blobs.
14 16
@@ -2,6 +2,8 @@
2 2
3 3 You can point your own domain at your Makenot.work profile so fans reach you at `yourdomain.com` instead of `makenot.work/u/username`.
4 4
5 + Setup is two DNS records: one to **route** your domain to Makenot.work, one to **verify** you own it. Add both, then click Verify.
6 +
5 7 ## Setup
6 8
7 9 ### 1. Add Your Domain
@@ -12,31 +14,33 @@ You can point your own domain at your Makenot.work profile so fans reach you at
12 14
13 15 Domain names must be valid hostnames. You cannot add `makenot.work` or its subdomains.
14 16
15 - ### 2. Verify Ownership
17 + ### 2. Point Your Domain
16 18
17 - Add a DNS TXT record to prove you own the domain:
19 + Add a CNAME record sending your domain to Makenot.work:
18 20
19 21 | Record Type | Host | Value |
20 22 |-------------|------|-------|
21 - | TXT | `_mnw-verify.yourdomain.com` | `mnw-verify-{verification-code}` |
22 -
23 - The verification code is shown on the settings page after adding your domain.
24 -
25 - After adding the DNS record, return to Settings > Domain and click "Verify." The platform checks via DNS-over-HTTPS (Cloudflare resolver), so verification completes within two minutes.
23 + | CNAME | `yourdomain.com` (or subdomain) | `connect.makenot.work` |
26 24
27 - ### 3. SSL Certificate
25 + > **If your DNS is on Cloudflare, set this record to "DNS only" (grey cloud), not "Proxied."** A proxied record routes through your own Cloudflare instead of to Makenot.work, which blocks both the SSL certificate and your page from loading.
28 26
29 - Once verified, Makenot.work provisions SSL automatically via on-demand TLS. A certificate is issued the first time a visitor connects.
27 + For an apex/root domain (`yourdomain.com` with no subdomain), most providers don't allow a plain CNAME — use their **CNAME flattening** or **ALIAS** feature pointed at `connect.makenot.work` (Cloudflare does this automatically). If your provider supports neither, use a subdomain like `shop.yourdomain.com` or `www.yourdomain.com`.
30 28
31 - ## DNS Configuration
29 + ### 3. Verify Ownership
32 30
33 - Point your domain to Makenot.work by adding a CNAME record:
31 + Add a DNS TXT record to prove you own the domain:
34 32
35 33 | Record Type | Host | Value |
36 34 |-------------|------|-------|
37 - | CNAME | `yourdomain.com` (or subdomain) | `makenot.work` |
35 + | TXT | `_mnw-verify.yourdomain.com` | `mnw-verify-{verification-code}` |
36 +
37 + The verification code is shown on the settings page after adding your domain.
38 +
39 + After adding both records, return to Settings > Domain and click "Verify." The platform checks via DNS-over-HTTPS (Cloudflare resolver), so verification completes within two minutes.
40 +
41 + ### 4. SSL Certificate
38 42
39 - If your DNS provider does not allow a CNAME on the apex domain, use their CNAME flattening or ALIAS record feature, or use a subdomain like `shop.yourdomain.com`.
43 + Once verified and routed, Makenot.work provisions SSL automatically via on-demand TLS — a Let's Encrypt certificate is issued the first time a visitor connects. No action needed on your end. (DNS changes can take up to 24 hours to propagate, so the first load may briefly show a certificate notice until the routing record resolves.)
40 44
41 45 ## Managing Your Domain
42 46
@@ -39,7 +39,7 @@ pub(super) async fn add_domain(
39 39 .await?;
40 40
41 41 let instructions = format!(
42 - "Add a DNS TXT record: <code>_mnw-verify.{}</code> with value <code>{}</code>",
42 + "Add two DNS records, then click Verify: a CNAME <code>{0}</code> &rarr; <code>connect.makenot.work</code> (set DNS-only / unproxied), and a TXT <code>_mnw-verify.{0}</code> with value <code>{1}</code>.",
43 43 domain, verification_token
44 44 );
45 45
@@ -137,7 +137,7 @@ pub(super) async fn get_domain(
137 137 String::new()
138 138 } else {
139 139 format!(
140 - "Add a DNS TXT record: _mnw-verify.{} with value {}",
140 + "Point {0} at connect.makenot.work (CNAME, DNS-only) and add a TXT _mnw-verify.{0} with value {1}, then verify.",
141 141 d.domain, d.verification_token
142 142 )
143 143 };
@@ -413,7 +413,7 @@ pub(super) async fn add_domain(
413 413 "domain": record.domain,
414 414 "verified": record.verified,
415 415 "verification_token": record.verification_token,
416 - "instructions": format!("Add a DNS TXT record: _mnw-verify.{} with value {}", record.domain, record.verification_token),
416 + "instructions": format!("Point {0} at connect.makenot.work (CNAME, DNS-only) and add a TXT _mnw-verify.{0} with value {1}, then verify.", record.domain, record.verification_token),
417 417 })))
418 418 }
419 419
@@ -69,7 +69,7 @@ pub(in crate::routes::pages::dashboard) async fn dashboard_tab_settings(
69 69 String::new()
70 70 } else {
71 71 format!(
72 - "Add a DNS TXT record: _mnw-verify.{} with value {}",
72 + "Point {0} at connect.makenot.work (CNAME, DNS-only) and add a TXT _mnw-verify.{0} with value {1}, then verify.",
73 73 d.domain, d.verification_token
74 74 )
75 75 };
@@ -145,7 +145,7 @@ pub(in crate::routes::pages::dashboard) async fn dashboard_tab_profile(
145 145 String::new()
146 146 } else {
147 147 format!(
148 - "Add a DNS TXT record: _mnw-verify.{} with value {}",
148 + "Point {0} at connect.makenot.work (CNAME, DNS-only) and add a TXT _mnw-verify.{0} with value {1}, then verify.",
149 149 d.domain, d.verification_token
150 150 )
151 151 };
@@ -154,9 +154,26 @@
154 154
155 155 {% if !cd.verified %}
156 156 <div class="callout callout--warning">
157 - <p class="m-0 mb-3"><strong>DNS setup required:</strong> Add a TXT record at your domain registrar.</p>
157 + <p class="m-0 mb-3"><strong>DNS setup required:</strong> add two records at your domain registrar, then click Check DNS.</p>
158 +
159 + <p class="m-0 mb-2"><strong>1. Point your domain</strong> (routes visitors to your page)</p>
160 + <div class="dns-row">
161 + <span class="dns-row-label">CNAME:</span>
162 + <code id="dns-route-host" class="dns-row-value">{{ cd.domain }}</code>
163 + <button type="button" class="btn-secondary dns-row-copy"
164 + onclick="navigator.clipboard.writeText(document.getElementById('dns-route-host').textContent).then(() => { this.textContent='Copied'; setTimeout(() => this.textContent='Copy', 1500) })">Copy</button>
165 + </div>
166 + <div class="dns-row">
167 + <span class="dns-row-label">Value:</span>
168 + <code id="dns-route-value" class="dns-row-value">connect.makenot.work</code>
169 + <button type="button" class="btn-secondary dns-row-copy"
170 + onclick="navigator.clipboard.writeText(document.getElementById('dns-route-value').textContent).then(() => { this.textContent='Copied'; setTimeout(() => this.textContent='Copy', 1500) })">Copy</button>
171 + </div>
172 + <p class="callout-hint mb-3">On Cloudflare, set this record to <strong>DNS only</strong> (grey cloud), not Proxied. For an apex/root domain, use CNAME flattening or ALIAS.</p>
173 +
174 + <p class="m-0 mb-2"><strong>2. Verify ownership</strong></p>
158 175 <div class="dns-row">
159 - <span class="dns-row-label">Record:</span>
176 + <span class="dns-row-label">TXT:</span>
160 177 <code id="dns-record" class="dns-row-value">_mnw-verify.{{ cd.domain }}</code>
161 178 <button type="button" class="btn-secondary dns-row-copy"
162 179 onclick="navigator.clipboard.writeText(document.getElementById('dns-record').textContent).then(() => { this.textContent='Copied'; setTimeout(() => this.textContent='Copy', 1500) })">Copy</button>