Skip to main content

max / makenotwork

Correct four comments that named things that do not exist The ChatPolicy doc asserted its gating as settled behaviour of the crate. It is not wired: ChatPolicy and chat_policy appear in exactly two places in mt, this enum and migration 037, and there is no chat route, no hub room and no chat write path. Mark it reserved and frame the four modes as the contract the Sprint 1 track has to satisfy, rather than as something a reader can rely on today. The same rewrite drops the near-verbatim copy of the migration header, which had already started to drift, and points at the migration for the composition rules instead. ALL was documented as ordered the way the owner settings UI offers the policies. There is no such UI and ALL has one consumer, a test, so the stated rule constrained nothing. It is least to most permissive, which is a rule a reader can check. The test harness credited its ConnectInfo fixture to SmartIpKeyExtractor, which is the extractor trusted_proxy.rs exists to replace. Name the live one and say what breaks without the fixture: every rate-limit test shares a single bucket. static_assets.rs cited deploy-hetzner.sh, deleted in be3070c0, the commit that added the file. docs/architecture.md carried the same stale extractor name in a worse place, documenting SmartIpKeyExtractor as the live key extractor in the rate-limiting section. That one reads as current security posture, and the extractor it names is the vulnerable one that trusts X-Forwarded-For from every request. Found by chasing the symbol, not by the audit, which appears to have scoped itself to .rs files.
Co-Authored-By
Claude Opus 5 (1M context) <noreply@anthropic.com>
Author: Max Johnson <me@maxj.phd> · 2026-08-05 16:59 UTC
Signed with PGP, not checked
Commit: 17ceaa5bf0c06e12b688aeb8fa4674f74a37bf19
Parent: f95c2b4
7 files changed, +192 insertions, -23 deletions
@@ -5009,18 +5009,6 @@
5009 5009 source = "registry+https://github.com/rust-lang/crates.io-index"
5010 5010 checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
5011 5011
5012 - [[patch.unused]]
5013 - name = "synckit-client"
5014 - version = "0.6.0"
5015 -
5016 - [[patch.unused]]
5017 - name = "synckit-config"
5018 - version = "0.1.2"
5019 -
5020 - [[patch.unused]]
5021 - name = "supernote-push"
5022 - version = "0.1.0"
5023 -
5024 5012 [[patch.unused]]
5025 5013 name = "kberg"
5026 5014 version = "0.1.0"
@@ -5028,3 +5016,15 @@
5028 5016 [[patch.unused]]
5029 5017 name = "painhours"
5030 5018 version = "0.1.0"
5019 +
5020 + [[patch.unused]]
5021 + name = "supernote-push"
5022 + version = "0.1.0"
5023 +
5024 + [[patch.unused]]
5025 + name = "synckit-client"
5026 + version = "0.6.0"
5027 +
5028 + [[patch.unused]]
5029 + name = "synckit-config"
5030 + version = "0.1.2"
@@ -22,6 +22,31 @@
22 22 | `mt-core` | Domain types and enums; no web or database dependencies |
23 23 | `mt-db` | PostgreSQL queries and mutations (sqlx) |
24 24
25 + ## Contributing
26 +
27 + This project is licensed under PolyForm Noncommercial 1.0.0. Make Creative, LLC owns
28 + the codebase and needs to hold clear title to all of it, so contributions work a
29 + little differently here than on a permissively licensed project.
30 +
31 + **Found a bug, or something small and obviously wrong?** Open a bug report rather
32 + than sending a patch. A clear report of what you did, what happened, and what you
33 + expected is worth more to us than a diff, and it saves you the paperwork below.
34 +
35 + **Want to build something substantial?** Write to info@makenot.work first. We are
36 + open to paying for contributions that matter, and we would rather agree on scope and
37 + terms before you spend your evenings on it than after.
38 +
39 + **Sending code directly?** Any code you submit is covered by the Contributor License
40 + Agreement in `CLA.md`, which assigns copyright in the contribution to Make Creative,
41 + LLC. Read it before you send anything. Add these lines to your commit message to
42 + record that you accept it:
43 +
44 + Make-Creative-CLA: 1.0.0
45 + Signed-off-by: Your Name <your@email>
46 +
47 + We may decline a contribution for any reason, including that it is not something we
48 + want to maintain.
49 +
25 50 ## License
26 51
27 52 PolyForm Noncommercial 1.0.0
@@ -134,7 +134,7 @@
134 134
135 135 - Burst: 10 requests
136 136 - Sustained: 2 requests/second (one token per 500ms)
137 - - Key extractor: `SmartIpKeyExtractor` (handles X-Forwarded-For behind reverse proxy)
137 + - Key extractor: `TrustedProxyKeyExtractor` (`src/trusted_proxy.rs`). It honors forwarding headers only when the direct peer is a configured trusted proxy, and otherwise keys on the peer address and ignores the headers. `tower_governor`'s stock `SmartIpKeyExtractor` is deliberately not used: it reads X-Forwarded-For from every request, so any direct client defeats the limiter by rotating the header.
138 138
139 139 Rate limiting is applied as a route layer on the write routes group only. Read routes have no rate limit. The internal API is also exempt (it uses HMAC auth, not sessions).
140 140
@@ -2,7 +2,8 @@
2 2 //!
3 3 //! `static/` used to be a directory beside the binary, served with
4 4 //! `ServeDir::new("static")` relative to the unit's `WorkingDirectory`, and
5 - //! `deploy/deploy-hetzner.sh` rsynced it on every deploy. Sando's companion
5 + //! the pre-Sando `deploy/deploy-hetzner.sh` (since removed) rsynced it on every
6 + //! deploy. Sando's companion
6 7 //! mechanism ships exactly one file, so keeping the assets on disk would have
7 8 //! meant shipping mt's binary with whatever CSS and JS happened to already be on
8 9 //! the box.
@@ -228,7 +228,8 @@
228 228 let mut request = builder
229 229 .body(Body::from(body_data))
230 230 .expect("Failed to build request");
231 - // Provide ConnectInfo so SmartIpKeyExtractor works in tests
231 + // TrustedProxyKeyExtractor keys on the direct peer, so without
232 + // ConnectInfo every rate-limit test shares one bucket
232 233 request
233 234 .extensions_mut()
234 235 .insert(ConnectInfo(SocketAddr::from(([127, 0, 0, 1], 0))));
@@ -130,19 +130,21 @@
130 130
131 131 /// Per-community chat policy (`communities.chat_policy`).
132 132 ///
133 - /// Read access here is on top of what the community already allows; chat never
134 - /// widens forum access. Write access is always additionally gated by
135 - /// `check_write_access` (not suspended, not banned, not muted) and by
136 - /// [`CommunityState`], which puts `Frozen` and `Archived` communities in
137 - /// read-only regardless of the policy.
133 + /// Reserved, not wired. The column exists and this enum round-trips it, but mt
134 + /// has no chat route, no hub room, and no chat write path, so nothing here
135 + /// gates anything yet. Chat lands in the Sprint 1 track (GoingsOn task
136 + /// `8f367108`); read the modes below as the contract that track must satisfy,
137 + /// not as current behaviour.
138 138 ///
139 139 /// - `Off`: no route, no hub room, no UI affordance. Nobody reads or writes.
140 140 /// - `Members`: anyone who can view the forum reads; members write.
141 141 /// - `PublicRead`: anyone reads, including logged out; members write.
142 142 /// - `FanPlus`: anyone who can view the forum reads; Fan+ writes.
143 143 ///
144 - /// The default is `Off`, including for communities that predate the column.
145 - /// Chat is a live moderation burden, so an owner opts into carrying it.
144 + /// Full semantics, including how these compose with `check_write_access` and
145 + /// [`CommunityState`], live in the header of `migrations/037_chat_policy.sql`.
146 + /// `Off` is the default for existing communities too: chat is a live moderation
147 + /// burden and an owner opts into carrying it.
146 148 #[derive(Debug, Clone, Copy, PartialEq, Eq, Default, sqlx::Type)]
147 149 #[sqlx(type_name = "TEXT", rename_all = "snake_case")]
148 150 pub enum ChatPolicy {
@@ -160,7 +162,7 @@
160 162 }
161 163
162 164 impl ChatPolicy {
163 - /// Every policy, in the order the owner settings UI offers them.
165 + /// Every policy, ordered least to most permissive.
164 166 pub const ALL: [Self; 4] = [Self::Off, Self::Members, Self::PublicRead, Self::FanPlus];
165 167
166 168 pub fn from_db(s: &str) -> Option<Self> {
@@ -1,0 +1,140 @@
1 + # Contributor License Agreement
2 +
3 + Read this before sending code. Most people should not need to: if you have found a
4 + bug, a clear bug report is more useful to us than a patch, and it saves you the
5 + paperwork below. See the contributing section of the README.
6 +
7 + This agreement is version 1.0.0. You agree to the version in effect on the day you
8 + send us code. A later revision does not change what you already agreed to.
9 +
10 + ## Make Creative Contributor License Agreement, version 1.0.0
11 +
12 + This agreement is between Make Creative, LLC, a Colorado limited liability company
13 + ("Make Creative", "we", "us"), and you, the person or entity submitting a
14 + Contribution ("you").
15 +
16 + ### 1. Definitions
17 +
18 + **Project** means any software project owned by Make Creative that distributes this
19 + agreement in its source repository.
20 +
21 + **Contribution** means any work of authorship you submit to a Project, in any form
22 + and through any channel: a patch sent by email, a diff attached to an issue, a pull
23 + or merge request on any repository or mirror, a commit pushed to a branch we grant
24 + you access to, or code pasted into a message. It covers source code, documentation,
25 + configuration, test data, and build files. It does not cover a bug report, a feature
26 + request, or a description of a problem that contains no code we could copy.
27 +
28 + **Submit** means to deliver a Contribution to us or to a Project by any of the means
29 + above.
30 +
31 + ### 2. Assignment
32 +
33 + You assign to Make Creative all right, title, and interest in and to your
34 + Contribution worldwide, including all copyrights and all rights to sue for past,
35 + present, and future infringement. The assignment is effective the moment you Submit,
36 + is irrevocable, and requires no further action by either of us.
37 +
38 + You will, at our request and our expense, sign any document reasonably needed to
39 + record or perfect the assignment.
40 +
41 + ### 3. Fallback license
42 +
43 + To the extent any part of the assignment in section 2 is unenforceable in your
44 + jurisdiction, or is later found ineffective, you instead grant Make Creative a
45 + perpetual, worldwide, irrevocable, exclusive, transferable, sublicensable,
46 + royalty-free license to reproduce, prepare derivative works of, publicly display,
47 + publicly perform, distribute, and otherwise exploit your Contribution, by any means
48 + and in any medium now known or later devised, for any purpose, commercial or
49 + noncommercial.
50 +
51 + To the extent permitted by law, you waive, and agree not to assert against Make
52 + Creative or anyone receiving the Contribution from us, any moral rights or rights of
53 + attribution or integrity in your Contribution. Where such a waiver is not permitted,
54 + you agree not to enforce those rights in a way that would interfere with our
55 + exercise of the rights granted here.
56 +
57 + ### 4. Patent license
58 +
59 + You grant Make Creative a perpetual, worldwide, irrevocable, transferable,
60 + sublicensable, royalty-free patent license to make, have made, use, offer to sell,
61 + sell, import, and otherwise transfer your Contribution and any Project incorporating
62 + it. The license covers only those patent claims you can license that are necessarily
63 + infringed by your Contribution alone or by its combination with the Project it was
64 + Submitted to.
65 +
66 + If you initiate patent litigation alleging that a Project or a Contribution
67 + infringes a patent, every license you have received from us under section 5
68 + terminates on the day the action is filed.
69 +
70 + ### 5. License back to you
71 +
72 + We grant you a perpetual, worldwide, irrevocable, non-exclusive, royalty-free
73 + license to use, reproduce, modify, and distribute your own Contribution, and
74 + derivative works of it, for any purpose, including commercially. This license covers
75 + your Contribution as you Submitted it. It does not extend to the rest of the
76 + Project, which remains available to you only under the Project's public license.
77 +
78 + ### 6. What you promise
79 +
80 + You represent that:
81 +
82 + 1. Each Contribution is your original work, or you have the right to Submit it and
83 + to grant the rights in this agreement.
84 + 2. If your employer has rights in work you produce, you have your employer's
85 + permission to Submit, or your employer has waived its rights in the Contribution.
86 + 3. Your Contribution does not contain, and is not derived from, code licensed under
87 + the GPL, LGPL, AGPL, or any other license that would impose conditions on how
88 + Make Creative may license the Project. If any part of your Contribution came from
89 + somewhere else, you identified the source and its license when you Submitted it.
90 + 4. If you used an automated tool to produce any part of your Contribution, you still
91 + hold or can grant the rights this agreement requires.
92 + 5. Your Contribution is not subject to any third-party agreement, license, or claim
93 + that conflicts with this agreement.
94 +
95 + If you learn that any of these is untrue, tell us at info@makenot.work promptly.
96 +
97 + ### 7. No warranty
98 +
99 + Except for section 6, you provide your Contribution as is, without warranty of any
100 + kind. You owe us no support, maintenance, or updates.
101 +
102 + ### 8. No obligation and no compensation
103 +
104 + We are not required to use, merge, distribute, or keep any Contribution. We may
105 + modify, relicense, or remove it. Submitting a Contribution creates no employment,
106 + partnership, agency, or joint venture between us.
107 +
108 + You are not entitled to compensation for a Contribution. If we agree to pay for
109 + one, that agreement will be separate and in writing, and it does not change the
110 + rights assigned here.
111 +
112 + ### 9. How you accept
113 +
114 + You accept this agreement when you Submit a Contribution. We also ask you to record
115 + that acceptance in each commit message:
116 +
117 + ```
118 + Make-Creative-CLA: 1.0.0
119 + Signed-off-by: Your Name <your@email>
120 + ```
121 +
122 + Leaving the line out does not undo your acceptance. It only means the record of it
123 + lives outside the repository.
124 +
125 + If you are Submitting on behalf of an entity, the person who Submits represents that
126 + they are authorized to bind that entity, and "you" means the entity.
127 +
128 + ### 10. Terms
129 +
130 + This agreement is governed by the laws of the State of Colorado, USA, without regard
131 + to its conflict of laws rules. Any dispute will be brought in the state or federal
132 + courts sitting in Colorado, and you consent to their jurisdiction.
133 +
134 + This is the entire agreement between us about Contributions, and it replaces any
135 + earlier understanding on the subject. If a provision is held unenforceable, the rest
136 + stays in force. Our failure to enforce a provision is not a waiver of it.
137 +
138 + We may publish a revised version of this agreement. A revision applies only to
139 + Contributions Submitted on or after its publication date. Your earlier Contributions
140 + stay governed by the version in effect when you Submitted them.