Skip to main content

max / makenotwork

Broadcast email streams, JSON-LD, copy-link, health page, and test infra Add Postmark broadcast stream support (separate webhook token, MessageStream routing for release announcements). Add JSON-LD structured data to audio player, text reader, and blog post templates. Add copy-link buttons to all public content pages. Expand health/status page with detailed checks. New test workflows for admin, git browser, and postmark webhooks. Bump to 0.1.8. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Author: Max J. <87768334+MaxJMath@users.noreply.github.com> · 2026-03-11 04:10 UTC
Commit: 6db86ed8fc109895496ae4cceb3077f8f41e8395
Parent: 1529342
43 files changed, +1489 insertions, -138 deletions
M CLAUDE.md +3 -3
@@ -20,9 +20,9 @@ server_code/makenotwork/ # Main Rust application
20 20 docs/ # Documentation (public/ and unpublished/)
21 21 ```
22 22
23 - ## Production Server (5.78.144.244)
23 + ## Production Server (hetzner)
24 24
25 - Hetzner VPS, x86_64 Linux. SSH as `root@5.78.144.244`.
25 + Hetzner VPS, x86_64 Linux. Tailscale hostname: `alpha-west-1` (IP: `100.120.174.96`). Public IP: `5.78.144.244`. SSH as `root@100.120.174.96` (via Tailscale).
26 26
27 27 ### Filesystem
28 28
@@ -102,7 +102,7 @@ psql -t -c "SELECT datname FROM pg_database WHERE datname LIKE 'mnw_test_%';" po
102 102
103 103 - **Stripe Connect** — payments (live mode)
104 104 - **Hetzner Object Storage** — S3-compatible file storage (fsn1 region)
105 - - **Postmark** — transactional email (password reset, verification, purchase receipts, notifications). Currently in trial mode: can only send to @makenot.work addresses until domain approval completes.
105 + - **Postmark** — transactional email (password reset, verification, purchase receipts, notifications). Live mode.
106 106 - **Cloudflare** — DNS, CDN, DDoS protection
107 107 - **Sentry** — error tracking
108 108 - **Fastmail** — business email (support@, legal@, max@)
@@ -4,8 +4,6 @@
4 4
5 5 Check if your question is answered:
6 6 - [FAQ](./faq.md)
7 - - [Troubleshooting](./troubleshooting.md)
8 - - [Documentation](./docs.md)
9 7
10 8 ## Email Support
11 9
@@ -39,9 +37,7 @@ Email support@makenot.work with detailed reproduction steps, or file an issue on
39 37
40 38 ## Feature Requests
41 39
42 - Have an idea?
43 - - [Feature Requests](./features.md) for suggestions
44 - - Platform forum for community input
40 + Have an idea? Email support@makenot.work with your suggestion.
45 41
46 42 ## What We Can't Help With
47 43
@@ -180,7 +180,7 @@ Not on Makenot.work. We recognize our infrastructure suits adult content well. M
180 180
181 181 ### What content isn't allowed?
182 182
183 - Content that dehumanizes, harasses, or incites violence toward any group. We hold content discussing marginalized groups to a higher standard. See [Acceptable Use](../../public/legal/acceptable-use.md).
183 + Content that dehumanizes, harasses, or incites violence toward any group. We hold content discussing marginalized groups to a higher standard. See [Acceptable Use](../legal/acceptable-use.md).
184 184
185 185 ### What's your stance on AI-generated content?
186 186
@@ -68,4 +68,4 @@ Drafts are private until you publish them.
68 68
69 69 - [Set up your profile](./profile.md)
70 70 - [Organize with tags](../creator/tag-hierarchy.md)
71 - - [Set up pricing](./pricing.md)
71 + - [Set up pricing](../../public/guide/07-pricing.md)
@@ -98,6 +98,6 @@ If you earn less on the platform than you paid in subscription fees during a 12-
98 98
99 99 ## See Also
100 100
101 - - [Pricing Overview](./pricing.md)
101 + - [Pricing Overview](../../public/guide/07-pricing.md)
102 102 - [Small Files Tier ($20/month)](./tier-small-files.md)
103 103 - [Setting Up Subscriptions](../creator/subscriptions.md)
@@ -128,6 +128,6 @@ If you earn less on the platform than you paid in subscription fees during a 12-
128 128
129 129 ## See Also
130 130
131 - - [Pricing Overview](./pricing.md)
131 + - [Pricing Overview](../../public/guide/07-pricing.md)
132 132 - [Streaming Tier ($40/month)](./tier-streaming.md)
133 133 - [Small Files Tier ($20/month)](./tier-small-files.md)
@@ -143,6 +143,6 @@ If you earn less on the platform than you paid in subscription fees during a 12-
143 143
144 144 ## See Also
145 145
146 - - [Pricing Overview](./pricing.md)
146 + - [Pricing Overview](../../public/guide/07-pricing.md)
147 147 - [Basic Tier ($10/month)](./tier-text.md)
148 148 - [Big Files Tier ($30/month)](./tier-big-files.md)
@@ -159,5 +159,5 @@ If you earn less on the platform than you paid in subscription fees during a 12-
159 159
160 160 ## See Also
161 161
162 - - [Pricing Overview](./pricing.md)
162 + - [Pricing Overview](../../public/guide/07-pricing.md)
163 163 - [Big Files Tier ($30/month)](./tier-big-files.md)
@@ -109,4 +109,4 @@ Beyond that, our decision is final. We're a small team and can't endlessly re-li
109 109
110 110 - [Content Moderation](./moderation.md) - how we make decisions
111 111 - [Account Enforcement](./enforcement.md) - enforcement actions
112 - - [Acceptable Use Policy](./acceptable-use.md) - what we enforce
112 + - [Acceptable Use Policy](../../public/legal/acceptable-use.md) - what we enforce
@@ -94,4 +94,4 @@ We track abuse patterns. Repeat false filers may be blocked from our DMCA proces
94 94
95 95 - [Counter-Notification](./dmca-counter.md) - disputing a takedown
96 96 - [Content Moderation](./moderation.md) - our broader moderation approach
97 - - [Terms of Service](./terms-of-service.md) - content ownership terms
97 + - [Terms of Service](../../public/legal/terms-of-service.md) - content ownership terms
@@ -139,6 +139,6 @@ Even terminated accounts can file appeals. We've overturned terminations when we
139 139
140 140 ## See Also
141 141
142 - - [Acceptable Use Policy](./acceptable-use.md) - what we enforce
142 + - [Acceptable Use Policy](../../public/legal/acceptable-use.md) - what we enforce
143 143 - [Content Moderation](./moderation.md) - how we make decisions
144 144 - [Appeal Process](./appeals.md) - disputing decisions
@@ -91,5 +91,5 @@ If you believe we made an error, contact appeals@makenot.work.
91 91
92 92 - [Creator Guarantees](../../public/about/guarantees.md)
93 93 - [Appeal Process](./appeals.md)
94 - - [Acceptable Use Policy](./acceptable-use.md)
94 + - [Acceptable Use Policy](../../public/legal/acceptable-use.md)
95 95 - [Transparency Reports](./transparency.md)
@@ -175,6 +175,6 @@ Conversion rates and timing vary by country. Check Stripe's documentation for sp
175 175
176 176 ## See Also
177 177
178 - - [Economics](../business/economics.md) - our pricing and cost structure
178 + - Economics (`docs/internal/business/economics.md`) - our pricing and cost structure
179 179 - [Creator Guarantees](../../public/about/guarantees.md) - our commitments on revenue
180 180 - [Stripe Documentation](https://stripe.com/docs) - for Stripe-specific questions
@@ -31,7 +31,7 @@ For software we run ourselves, we prefer open source. Managed services sometimes
31 31
32 32 We can explain every line item in our infrastructure bill. When creators ask where their subscription money goes, we have clear answers.
33 33
34 - See [Economics](../business/economics.md) for the breakdown.
34 + See the economics documentation for the breakdown.
35 35
36 36 ---
37 37
@@ -145,5 +145,5 @@ We accept these trade-offs because the alternative — expensive vendor lock-in
145 145
146 146 - [Architecture](./architecture.md) — system design and components
147 147 - [Security](./security.md) — how we protect data
148 - - [Economics](../business/economics.md) — where infrastructure costs fit in
149 - - [Partnerships](../business/partnerships.md) — our approach to vendor relationships
148 + - Economics (`docs/internal/business/economics.md`) — where infrastructure costs fit in
149 + - Partnerships (`docs/internal/business/partnerships.md`) — our approach to vendor relationships
@@ -3453,7 +3453,7 @@ dependencies = [
3453 3453
3454 3454 [[package]]
3455 3455 name = "makenotwork"
3456 - version = "0.1.6"
3456 + version = "0.1.7"
3457 3457 dependencies = [
3458 3458 "ammonia",
3459 3459 "anyhow",
@@ -1,6 +1,6 @@
1 1 [package]
2 2 name = "makenotwork"
3 - version = "0.1.7"
3 + version = "0.1.8"
4 4 edition = "2024"
5 5 license-file = "../../LICENSE"
6 6
@@ -56,7 +56,7 @@ openssl rand -base64 24
56 56 ### 2. Initial Server Setup
57 57 ```bash
58 58 # SSH into server
59 - ssh root@5.78.144.244
59 + ssh root@100.120.174.96
60 60
61 61 # Update system
62 62 apt update && apt upgrade -y
@@ -118,19 +118,19 @@ chown -R makenotwork:makenotwork /opt/makenotwork
118 118 From your local machine:
119 119 ```bash
120 120 # Copy Caddyfile
121 - scp deploy/Caddyfile root@5.78.144.244:/etc/caddy/Caddyfile
121 + scp deploy/Caddyfile root@100.120.174.96:/etc/caddy/Caddyfile
122 122
123 123 # Copy systemd service
124 - scp deploy/makenotwork.service root@5.78.144.244:/etc/systemd/system/
124 + scp deploy/makenotwork.service root@100.120.174.96:/etc/systemd/system/
125 125
126 126 # Copy environment template
127 - scp deploy/env.production root@5.78.144.244:/opt/makenotwork/.env
127 + scp deploy/env.production root@100.120.174.96:/opt/makenotwork/.env
128 128 ```
129 129
130 130 ### 7. Configure Environment
131 131 ```bash
132 132 # SSH into server
133 - ssh root@5.78.144.244
133 + ssh root@100.120.174.96
134 134
135 135 # Edit .env with your actual values
136 136 nano /opt/makenotwork/.env
@@ -172,16 +172,16 @@ From your local machine in `server_code/makenotwork/`:
172 172 chmod +x deploy/deploy.sh
173 173
174 174 # Deploy — cross-compiles for x86_64 Linux, uploads binary, restarts service
175 - ./deploy/deploy.sh root@5.78.144.244
175 + ./deploy/deploy.sh root@100.120.174.96
176 176 ```
177 177
178 178 ### Verify Deployment
179 179 ```bash
180 180 # Check service status
181 - ssh root@5.78.144.244 "systemctl status makenotwork"
181 + ssh root@100.120.174.96 "systemctl status makenotwork"
182 182
183 183 # Check logs
184 - ssh root@5.78.144.244 "journalctl -u makenotwork -f"
184 + ssh root@100.120.174.96 "journalctl -u makenotwork -f"
185 185
186 186 # Test endpoints
187 187 curl https://makenot.work/
@@ -261,16 +261,16 @@ cat /etc/postgresql/*/main/pg_hba.conf | grep makenotwork
261 261
262 262 ### Update Application
263 263 ```bash
264 - ./deploy/deploy.sh root@5.78.144.244
264 + ./deploy/deploy.sh root@100.120.174.96
265 265 ```
266 266
267 267 ### View Logs
268 268 ```bash
269 269 # Application logs
270 - ssh root@5.78.144.244 "journalctl -u makenotwork -f"
270 + ssh root@100.120.174.96 "journalctl -u makenotwork -f"
271 271
272 272 # Caddy logs
273 - ssh root@5.78.144.244 "tail -f /var/log/caddy/makenotwork.log"
273 + ssh root@100.120.174.96 "tail -f /var/log/caddy/makenotwork.log"
274 274 ```
275 275
276 276 ### Database Backups
@@ -280,6 +280,6 @@ For recovery procedures, see `RECOVERY.md`.
280 280
281 281 ### Restart Services
282 282 ```bash
283 - ssh root@5.78.144.244 "sudo systemctl restart makenotwork"
284 - ssh root@5.78.144.244 "sudo systemctl restart caddy"
283 + ssh root@100.120.174.96 "sudo systemctl restart makenotwork"
284 + ssh root@100.120.174.96 "sudo systemctl restart caddy"
285 285 ```
@@ -16,7 +16,7 @@
16 16 set -e
17 17
18 18 # Configuration
19 - SERVER="root@5.78.144.244"
19 + SERVER="root@100.120.174.96"
20 20 REMOTE_DIR="/opt/makenotwork"
21 21 BINARY_NAME="makenotwork"
22 22 TARGET="x86_64-unknown-linux-gnu"
@@ -307,6 +307,7 @@ mod tests {
307 307 scan: None,
308 308 git_repos_path: None,
309 309 postmark_webhook_token: None,
310 + postmark_broadcast_webhook_token: None,
310 311 };
311 312 assert!(require_admin(&user, &config).is_ok());
312 313 }
@@ -353,6 +354,7 @@ mod tests {
353 354 scan: None,
354 355 git_repos_path: None,
355 356 postmark_webhook_token: None,
357 + postmark_broadcast_webhook_token: None,
356 358 };
357 359 assert!(require_admin(&user, &config).is_err());
358 360 }
@@ -35,6 +35,8 @@ pub struct Config {
35 35 pub git_repos_path: Option<String>,
36 36 /// Bearer token for authenticating Postmark webhook requests (optional)
37 37 pub postmark_webhook_token: Option<String>,
38 + /// Bearer token for authenticating Postmark broadcast stream webhooks (optional)
39 + pub postmark_broadcast_webhook_token: Option<String>,
38 40 }
39 41
40 42 /// S3-compatible storage configuration (Hetzner Object Storage)
@@ -117,6 +119,9 @@ impl Config {
117 119 // Postmark webhook token - optional, webhook endpoint returns 401 if unset
118 120 let postmark_webhook_token = std::env::var("POSTMARK_WEBHOOK_TOKEN").ok();
119 121
122 + // Postmark broadcast stream webhook token - optional, same endpoint accepts either token
123 + let postmark_broadcast_webhook_token = std::env::var("POSTMARK_BROADCAST_WEBHOOK_TOKEN").ok();
124 +
120 125 Ok(Config {
121 126 host,
122 127 port,
@@ -132,6 +137,7 @@ impl Config {
132 137 scan,
133 138 git_repos_path,
134 139 postmark_webhook_token,
140 + postmark_broadcast_webhook_token,
135 141 })
136 142 }
137 143
@@ -257,6 +263,7 @@ impl std::fmt::Debug for Config {
257 263 .field("scan", &self.scan)
258 264 .field("git_repos_path", &self.git_repos_path)
259 265 .field("postmark_webhook_token", &self.postmark_webhook_token.as_ref().map(|_| "[REDACTED]"))
266 + .field("postmark_broadcast_webhook_token", &self.postmark_broadcast_webhook_token.as_ref().map(|_| "[REDACTED]"))
260 267 .finish()
261 268 }
262 269 }
@@ -316,6 +323,7 @@ mod tests {
316 323 scan: None,
317 324 git_repos_path: None,
318 325 postmark_webhook_token: None,
326 + postmark_broadcast_webhook_token: None,
319 327 };
320 328 let addr = config.socket_addr();
321 329 assert_eq!(addr.port(), 8080);