Skip to content

Commit 6df5e3e

Browse files
docs(site): publish a terms of service page (#273)
lux has shipped on two app stores, syncs accounts, hands one person control of another person's lights, and now reads a church's service plan — with no terms anywhere. This adds /terms/ as a third real document (same MPA shape as /privacy/) and links it from the nav and the footer beside Privacy. Covers what exists today and nothing else: the app is free and GPLv3 with no account required, there is nothing to buy, accounts and shared control (including the up-to-an-hour revocation window a guest connection carries), Planning Center as read-only and cancellable from either end, the lighting posture — local control is authoritative, keep an operator, not for life-safety — availability with no SLA, your content, liability, and changes.
1 parent 13e7427 commit 6df5e3e

5 files changed

Lines changed: 276 additions & 2 deletions

File tree

apps/site/src/chrome.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,16 @@ export function Nav() {
88
</svg>
99
lux
1010
</a>
11-
<div className="flex items-center gap-7 text-[15px]">
11+
<div className="flex items-center gap-5 text-[15px] sm:gap-7">
1212
<a className="text-mut transition-colors hover:text-ink" href="/#how">
1313
How it works
1414
</a>
1515
<a className="text-mut transition-colors hover:text-ink" href="/privacy/">
1616
Privacy
1717
</a>
18+
<a className="text-mut transition-colors hover:text-ink" href="/terms/">
19+
Terms
20+
</a>
1821
</div>
1922
</nav>
2023
);
@@ -38,6 +41,9 @@ export function Footer() {
3841
<a className="transition-colors hover:text-ink" href="/privacy/">
3942
Privacy
4043
</a>
44+
<a className="transition-colors hover:text-ink" href="/terms/">
45+
Terms
46+
</a>
4147
</div>
4248
</div>
4349
</footer>

apps/site/src/pages/terms.tsx

Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
import { Nav, Footer } from "../chrome";
2+
3+
export function Terms() {
4+
return (
5+
<div className="mx-auto max-w-[1120px] px-6">
6+
<Nav />
7+
8+
<article className="mx-auto max-w-[68ch] py-14 pb-24 [&_h2]:mt-10 [&_h2]:mb-2.5 [&_h2]:text-2xl [&_h2]:font-semibold [&_li]:my-1.5 [&_li]:text-[#c6ccd4] [&_p]:my-3 [&_p]:text-[#c6ccd4] [&_strong]:text-ink [&_ul]:my-3 [&_ul]:ml-6 [&_ul]:list-disc">
9+
<h1 className="text-[clamp(32px,4vw,44px)] font-bold tracking-[-0.02em]">
10+
Terms
11+
</h1>
12+
<p className="mt-1.5 !text-[15px] !text-mut">
13+
Effective August 10, 2026 · applies to lux for macOS and iOS, the lux
14+
account, and this site
15+
</p>
16+
17+
<h2>The short version</h2>
18+
<p>
19+
The app is free and open source, and it works without an account. An
20+
account exists only for the things that cross a boundary — another
21+
device, another person, another company's service. Two sections below
22+
are worth reading twice:{" "}
23+
<strong>lighting control</strong> and{" "}
24+
<strong>Planning Center</strong>. They are the ones that matter when
25+
something goes wrong during a service.
26+
</p>
27+
28+
<h2>Who you're agreeing with</h2>
29+
<p>
30+
lux is made and run by John Carmack —{" "}
31+
<a className="text-accent underline underline-offset-3" href="mailto:johncarmack@me.com">
32+
johncarmack@me.com
33+
</a>
34+
. "We" means that. "You" means you, and if you're agreeing on behalf
35+
of a church or an organization, it means that organization and you're
36+
confirming you may bind it.
37+
</p>
38+
39+
<h2>The app is free, and it's yours</h2>
40+
<p>
41+
The lux desktop and mobile apps are free and open source under the
42+
GNU General Public License v3.0. That license governs the software
43+
itself, and{" "}
44+
<strong>nothing in these terms takes away a right it grants you</strong>.
45+
The full 512-channel desk, all three outputs, custom fixtures,
46+
scenes, and manual control work with no account and no payment.
47+
</p>
48+
49+
<h2>There's nothing to buy</h2>
50+
<p>
51+
lux has no paid plan, no subscription, and no in-app purchase. If
52+
that ever changes, the terms for it will be published here before
53+
anyone is asked to pay, and the offline app will stay free.
54+
</p>
55+
56+
<h2>Accounts</h2>
57+
<p>
58+
You need an account only for the features that leave your machine:
59+
syncing setups between your devices, sharing control with someone
60+
else, connecting Planning Center. You're responsible for what happens
61+
under yours. Tell us at the address above if you think someone else
62+
has it.
63+
</p>
64+
<p>
65+
Don't hand out your password to share a rig — share the setup
66+
instead. That's a thing lux does properly, and it's revocable.
67+
</p>
68+
69+
<h2>Sharing control</h2>
70+
<p>
71+
Sharing a setup gives another lux account real control of real lights
72+
on your rig. Give it only to people you trust, and know what it costs
73+
to take back:
74+
</p>
75+
<ul>
76+
<li>
77+
An invite is single-use and expires on its own. Either side can end
78+
a grant at any time — revoking and leaving are the same act named
79+
from opposite ends, and neither party needs the other's
80+
cooperation.
81+
</li>
82+
<li>
83+
<strong>Revoking is not instant for a guest who is already
84+
connected.</strong> Their access is checked when they connect, and
85+
their connection re-checks about once an hour, so someone already
86+
at your desk may keep it until then. If you need them off the rig
87+
right now, pull the setup off the network or close the app.
88+
</li>
89+
</ul>
90+
91+
<h2>Planning Center</h2>
92+
<p>
93+
If you connect Planning Center, you're confirming you're allowed to
94+
do that for your organization.
95+
</p>
96+
<ul>
97+
<li>
98+
<strong>lux only reads.</strong> It never creates, edits, or
99+
deletes anything in Planning Center, and it never advances your
100+
service. That's a property of how the software is built, not only a
101+
promise — the Privacy Policy says exactly how.
102+
</li>
103+
<li>
104+
lux reads your service types and, for the one you pick, the next
105+
plan and its items. Precisely what that means is in the{" "}
106+
<a className="text-accent underline underline-offset-3" href="/privacy/">
107+
Privacy Policy
108+
</a>
109+
.
110+
</li>
111+
<li>
112+
Planning Center is your provider, not ours. Your relationship with
113+
them, and their availability, are between you and them. If they're
114+
down, change their API, or end your access, lux's plan features
115+
stop — and, per the next section, your lights do not.
116+
</li>
117+
<li>
118+
Disconnect at any time, from lux or from Planning Center. Either
119+
one ends it.
120+
</li>
121+
</ul>
122+
123+
<h2>Lighting control — read this one twice</h2>
124+
<p>
125+
<strong>lux is a tool, and you are the operator.</strong>
126+
</p>
127+
<ul>
128+
<li>
129+
<strong>Local control is authoritative and doesn't depend on
130+
us.</strong> Live DMX output goes from your machine to your
131+
hardware. If our servers are down, your internet is out, or
132+
Planning Center is unreachable, your lights hold and manual control
133+
keeps working. That's how lux is built, and it's the reason the
134+
cloud features are allowed to exist at all.
135+
</li>
136+
<li>
137+
The plan view shows you your running order. It does not touch your
138+
lights on its own — you're the one pressing Go — and nothing in it
139+
sits in the path between a fader and a fixture.
140+
</li>
141+
<li>
142+
<strong>Keep a person who can take over.</strong> For any service
143+
that matters, have someone at the desk who can run it by hand, and
144+
make sure they know how.
145+
</li>
146+
<li>
147+
<strong>Do not use lux where a lighting failure could hurt
148+
someone</strong>, or where a specific light must be guaranteed on
149+
or off for safety. It is not certified for life-safety, emergency
150+
lighting, or anything with that kind of consequence. Strobing and
151+
rapid intensity changes can trigger photosensitive seizures; what
152+
you put in front of an audience is your call and your
153+
responsibility.
154+
</li>
155+
</ul>
156+
157+
<h2>Acceptable use</h2>
158+
<p>
159+
Don't use lux to break the law, don't try to get into other people's
160+
accounts or setups, and don't attack the service.
161+
</p>
162+
163+
<h2>Availability</h2>
164+
<p>
165+
We try to keep the cloud features up, but there's no promised uptime
166+
and no SLA. We may change or retire a cloud feature with reasonable
167+
notice. <strong>The offline app is the guarantee; the servers are the
168+
convenience.</strong>
169+
</p>
170+
171+
<h2>Your content</h2>
172+
<p>
173+
Your setups, your scenes, and your plan data are yours. We claim no
174+
ownership of them and no license beyond storing and transmitting them
175+
to give you the features you turned on. Deleting your account removes
176+
them from our servers — the{" "}
177+
<a className="text-accent underline underline-offset-3" href="/privacy/">
178+
Privacy Policy
179+
</a>{" "}
180+
says what that reaches, and what you should end yourself first.
181+
</p>
182+
183+
<h2>Warranties and liability</h2>
184+
<p>
185+
The hosted service is provided "as is", without warranties beyond
186+
those the law doesn't let us disclaim. (The software itself carries
187+
the GPL's own warranty disclaimer; this covers the servers.) To the
188+
extent the law allows, we're not liable for indirect or consequential
189+
losses — a missed cue, a service that didn't look the way you wanted,
190+
lost volunteer time. Nothing here limits liability for death or
191+
personal injury caused by negligence, or for anything else that can't
192+
be limited.
193+
</p>
194+
195+
<h2>Ending it</h2>
196+
<p>
197+
Stop using it whenever you like; delete the account from the account
198+
menu. We may suspend or end an account that breaks these terms, and
199+
we'll say why unless we're not allowed to. When it ends, the cloud
200+
features stop and the free app carries on.
201+
</p>
202+
203+
<h2>Changes</h2>
204+
<p>
205+
If these terms change, the new text lands on this page with a new
206+
date at the top. If a change is material and you have an account,
207+
we'll tell you before it takes effect.
208+
</p>
209+
210+
<h2>Contact</h2>
211+
<p>
212+
Questions:{" "}
213+
<a className="text-accent underline underline-offset-3" href="mailto:johncarmack@me.com">
214+
johncarmack@me.com
215+
</a>
216+
</p>
217+
</article>
218+
219+
<Footer />
220+
</div>
221+
);
222+
}

apps/site/src/terms-main.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { StrictMode } from "react";
2+
import { createRoot } from "react-dom/client";
3+
import "./app.css";
4+
import { Terms } from "./pages/terms";
5+
6+
createRoot(document.getElementById("root")!).render(
7+
<StrictMode>
8+
<Terms />
9+
</StrictMode>,
10+
);

apps/site/terms/index.html

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6+
<title>Terms — lux</title>
7+
<meta
8+
name="description"
9+
content="The terms for the lux apps and the lux account: free and open source, no account required, and who is responsible for what when the lights are on."
10+
/>
11+
<link rel="canonical" href="https://lux.johncarmack.com/terms/" />
12+
<meta property="og:title" content="Terms — lux" />
13+
<meta
14+
property="og:description"
15+
content="Free and open source, no account required, and who is responsible for what when the lights are on."
16+
/>
17+
<meta property="og:url" content="https://lux.johncarmack.com/terms/" />
18+
<meta name="theme-color" content="#0b0d10" />
19+
<link
20+
rel="icon"
21+
href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Cpolygon points='16,6 26,28 6,28' fill='%23e8a33d' fill-opacity='0.35'/%3E%3Ccircle cx='16' cy='7' r='4' fill='%23e8a33d'/%3E%3C/svg%3E"
22+
/>
23+
<link
24+
rel="preload"
25+
href="/fonts/Geist-Variable.woff2"
26+
as="font"
27+
type="font/woff2"
28+
crossorigin
29+
/>
30+
</head>
31+
<body>
32+
<div id="root"></div>
33+
<script type="module" src="/src/terms-main.tsx"></script>
34+
</body>
35+
</html>

apps/site/vite.config.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import react, { reactCompilerPreset } from "@vitejs/plugin-react";
44
import babel from "@rolldown/plugin-babel";
55
import tailwindcss from "@tailwindcss/vite";
66

7-
// MPA on purpose: two real HTML documents (home + privacy), so the privacy URL
7+
// MPA on purpose: real HTML documents (home, privacy, terms), so each legal URL
88
// resolves as a document with no client routing involved.
99
export default defineConfig({
1010
plugins: [react(), babel({ presets: [reactCompilerPreset()] }), tailwindcss()],
@@ -13,6 +13,7 @@ export default defineConfig({
1313
input: {
1414
main: resolve(import.meta.dirname, "index.html"),
1515
privacy: resolve(import.meta.dirname, "privacy/index.html"),
16+
terms: resolve(import.meta.dirname, "terms/index.html"),
1617
},
1718
},
1819
},

0 commit comments

Comments
 (0)