max / makenotwork
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> → <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> |