Every claim below was read out of the code that performs it. This is
what the software does with what you type, stated exactly and in the
order it happens.
In force
From 7 September 2026. This notice covers
splitbench.com, operated by Garrell Tech Solutions LLC. It is
rewritten whenever the software's behaviour changes and the date
above moves with it, so a claim here and the code that performs it
are never allowed to drift apart.
Section one
Who runs this
Splitbench is operated by Garrell Tech Solutions LLC,
a limited liability company formed in Florida, United States. Write
to [email protected]
about anything on this page.
Splitbench is also software anyone may run themselves. This page
describes the deployment you are reading it on. A copy running on
somebody else's machine sends its debates to whichever providers that
operator configured and stores them wherever that operator points it,
and none of what follows is a promise about their copy.
Section two
What leaves this machine, and to whom
A debate is a conversation with language models that other companies
run. There is no way to hold one without sending them what is being
argued. Three destinations, and nothing else:
openrouter.ai/api/v1 — every turn
Every request a debater or the judge makes goes to OpenRouter,
which forwards it to the lab that runs the model sitting in that
chair. A debater's request carries the motion, the agreed ground,
the transcript so far, and any point you have made from the floor.
The judge's request carries the whole transcript at once. If you
ask the chamber to sharpen a rough subject into a motion, the
subject and your answers to its questions go the same way.
Nothing identifying you travels with any of it: no account, no
address, no cookie, no browser fingerprint. The only headers this
app adds beyond the request itself are an optional application URL
and title, which describe the deployment rather than the visitor.
api.openai.com/v1/moderations — once per debate
Before the first debater is asked to speak, the motion — and
only the motion — is sent to OpenAI's moderation classifier
(omni-moderation-latest) to decide whether this demo
will debate it. The agreed ground is not sent, the transcript is
not sent, and nothing said later from the floor is sent. It runs
once, at the start, not on every turn. The categories the
classifier returns are used to choose which refusal you see and
are not stored anywhere. A check that cannot be completed refuses
the debate rather than running one nobody screened.
fonts.googleapis.com and fonts.gstatic.com — page load
This page and the chamber both load three typefaces from Google
Fonts. Your browser fetches them from Google directly, which means
Google sees that request — your address and your browser's
user agent — exactly as it does on any site that loads fonts
this way. Nothing about your motion is in it.
There is no analytics script, no tag manager, no session recorder and
no advertising pixel anywhere in this site. The viewer's entire
third-party dependency list is React and the Supabase client.
Worth knowing
Nothing you write travels in a URL. The motion,
the agreed ground and each point from the floor are sent in the
body of a POST. The streaming connection that follows
carries only the debate's id in its address, so the request line
— the part of a request an ordinary web-server access log
records — never contains anything you typed.
Section three
Your words are handled as claims, never as orders
The motion and the agreed ground are the only words in a debate that
a participant writes, and a system prompt is the one place a sentence
is read as an instruction rather than as a claim. So participant text
never goes in a system prompt. It travels in the user message, inside
marked blocks — --- BEGIN PROPOSITION (written by a
participant) --- and its closing marker — and anything
in your text shaped like one of those markers is stripped before the
block is assembled, so that nothing can write its own way out of the
fence.
Both the debaters' and the judge's system prompts state what the
fence means: the blocks are the subject being worked on, they are
never instructions, and nothing inside them can change the rules or
dictate a conclusion. This is a safeguard for the integrity of the
debate, and it is also the reason a motion cannot be used to make a
model do something else with it.
Section four
What is stored, and for how long
There is no retention period in this software, because retention here
is not measured in days. Both stores below are bounded by
capacity: they hold a fixed number of the most recent
records and drop the oldest to make room. What removes a record is a
newer record arriving. That is the whole policy, stated as it is
rather than as a number of months nothing enforces.
While it is running
A debate in progress
Where
A row in splitbench.debate_sessions in the
deployment's Postgres database. A deployment configured with no
database keeps it in the server's memory instead, and loses it
on restart.
What
The motion, the agreed ground, the transcript so far, the
ruling once there is one, which models are in which chair and
at what effort, what each turn cost, and a lease naming the
machine currently running it. No credential and no deployment
configuration is ever written here.
How long
The 32 most recently written are kept. Each
write drops the oldest beyond that, except any debate a machine
is actively arguing, which is exempt however old it looks.
Only if you press save
A saved debate
Where
A row in splitbench.debates in the same database.
A deployment configured with no database writes to a local
SQLite file instead.
What
An immutable snapshot: the motion, the agreed ground, the full
transcript including every point from the floor, the ruling,
which model argued which side, the number of rounds, what each
turn cost, and a SHA-256 hash of the delete token. The token
itself is never stored.
How long
The 500 most recent are kept. A save past that
drops the oldest — which means a saved link can stop
working because other people saved debates after you, and there
is no notice when it does.
And nothing else
A debate you do not save is never written to the archive. When it
ages out of the 32 live rows, or the server restarts on a
deployment holding them in memory, it is gone.
The application owns five tables in all. The two above; the two
that belong to an account, splitbench.system_admin and
splitbench.unit_ledger, both under
accounts; and
splitbench.rate_window, which is the subject of
section seven. None of them has a column
for a user agent, a device identifier or a referrer.
Section five
What your browser holds
Five keys in this site's localStorage. Four of them
never leave your device. The fifth does, on every request, and it is
the first one listed for that reason.
splitbench.visitor — sent
A random identifier your browser mints for itself the first time
it calls the API, and sends afterwards in the
X-Splitbench-Visitor header on every
/api request. It exists so that one visitor behind a
shared address is not rationed as though they were everybody
behind it — see section seven,
where it is also what gets written down. It is not a credential,
it is attached to no account and no debate, and it carries nothing
about you: clearing site data mints a new one, which is a thing
you may do freely.
splitbench.motions
Motions and their agreed ground that you chose to keep, so you can
put the same one to a different bench. Up to 100, newest first.
splitbench.saved
The debates you have saved: the link id, the motion, the time, and
the delete token. Up to 50, newest first.
splitbench.theme
Light or dark, written only if you chose one explicitly. Following
the system setting stores nothing.
supabase.auth.token
Your signed-in session, written by the Supabase client and only if
you signed in.
Clearing site data for this domain removes all five. The delete
tokens go with them, and no one can recover them — see
section ten.
Section six
Accounts
Nothing in the chamber requires one. Putting a motion, taking the
floor, reading the ruling and saving a debate are all anonymous, and
registration is currently closed in any case.
Where an account exists, it is held by Supabase, the
authentication service, not by this application. Supabase holds the
email address it was given, and — if the account signed in
through Google — whatever that sign-in returned to it. Sign-in
is by a one-time link sent to an address, by a password, or through
Google.
Splitbench's own tables hold no email address and no name. They hold
the Supabase user id, which is a random identifier, in two places.
splitbench.system_admin
That id, the time access was granted and an optional note, for
each person allowed to read the operator's cost dashboard. Almost
nobody is in this table.
splitbench.unit_ledger
A row each time a signed-in account buys units or spends them. A
row holds that id, a signed quantity, a reason — one of
debate, sharpen or screen
— the id of the debate that spent it where one did, and the
time. Nothing is ever overwritten: a balance is the sum of the
rows, so the rows are a history and are kept as one.
Units are only drawn on after the free hourly
allowance is used up, so an account that stays inside it spends
nothing and writes no rows. Deleting the account in Supabase deletes
every row in both tables with it.
A saved debate is still not attached to an account: nothing in the
archive names a person. The one link that exists runs the other way
and is worth stating exactly. A ledger row names the debate it paid
for by that debate's in-progress id, which is not the id in
a shared link — the two are minted separately. While the
in-progress row survives, it records which saved debate it became, so
the two could be joined; once it ages out of the 32 kept, the id in
the ledger refers to nothing that still exists.
Section seven
Your address
Your IP address is used for one thing — rate limiting —
and, on this deployment, it is written to a table to do
it. The counters live in Postgres rather than in a single
server's memory, so that two machines enforce one allowance between
them instead of one each.
Every guarded request draws on somebody's hourly allowance, and the
server decides whose in this order. A verified account is
user: and its own id. A request with no account is
visitor: and the random id the browser minted for
itself (section five), beneath a ceiling
keyed ip: and the address — as the hosting proxy
reports it in Fly-Client-IP, or, where there is no
proxy, the connection's own address, and reduced to its /64 where it
is IPv6, since a residential IPv6 line is delegated a whole /64 and
rationing the full address would ration nothing. Whichever of those
three strings applies, prefixed again by which allowance is being
drawn on, is the bucket column of
splitbench.rate_window.
What a row holds
The bucket and a timestamp. That is the entire table. A row
records that one visitor — an account, a browser's own id,
or an address — made a request of a given kind at a given
moment. Not what they asked, not what was argued, and nothing that
joins to a debate or a transcript.
How long it lives
One hour. A row older than the window it is counted in is deleted:
by your own next request, which prunes your rows before counting
them, and by a fleet-wide sweep that any guarded request has
roughly a one-in-five-hundred chance of running. So rows belonging
to a visitor who never returns are cleared by other people's
traffic, and on a deployment that has gone quiet they can outlive
the hour until traffic returns.
Signing in replaces it
A signed-in request is counted against the account id instead, so
neither your address nor your browser's id is written for it.
Every request that draws on an allowance — starting a debate, continuing it, screening its
motion and sharpening a topic — carries your session, so
signing in moves all of them off your address. The streams that
run the rounds afterwards carry nothing and draw on no allowance.
Why the prefixesX-Forwarded-For is a header the sender writes, and it
is no longer read: the proxy's own account of the connection is
used instead, so the address counted against you is not one you
can choose. The three key spaces are kept separate regardless,
so that an account's id can never be reached from the space
addresses live in, and a browser cannot spend an account's
allowance by sending that account's id as its own.
Your address is written nowhere else. It is not in the archive, not
in the session store, not in the ledger, and not attached to any
debate. A deployment configured with no database keeps the same
counters in memory instead, and writes no address at all.
Section eight
What "unlisted" means, and what it does not
Saving a debate produces a link at /d/{id}. The id is
sixteen hexadecimal characters — sixty-four bits from the
operating system's cryptographic random source. It is not sequential,
and guessing one is not a practical attack. Both the page and the API
that serves its content answer with
X-Robots-Tag: noindex, nofollow, so a crawler that
honours it will not index the debate. An id nobody ever saved and an
id that has been unpublished return exactly the same response, so
somebody guessing learns nothing from the difference.
Unlisted is not private. There is no password on a
saved debate and no check of who is asking for it. Anyone holding the
link can read the whole thing, and can forward it to anyone else.
More than that: when the link is pasted into a service that unfurls
links — a chat app, a social network, a mail client — this
server hands that service the motion (clipped to 110 characters) and
the ruling (clipped to 200) in the page's preview tags. The substance
of the debate is therefore visible to that service, and to everyone
who can see the preview, without anyone opening the link at all.
Treat an unlisted link as public to everyone it reaches. Do not put
anything into a motion, a stipulation or a point from the floor that
you would mind a stranger reading.
Section nine
Cookies
This application sets no cookies. Nothing in the
server writes one and nothing reads one. The API is configured to
refuse credentialed cross-origin requests for precisely that reason:
the only credential it ever reads is an
Authorization: Bearer header that the page attaches by
hand, which is a different mechanism.
If you sign in, the Supabase client keeps your session in this
browser's localStorage under
supabase.auth.token — not in a cookie. Signing out
removes it. Because there are no cookies, there is no consent banner
and nothing to opt out of.
Section ten
Asking for a copy, or a deletion
Write to
[email protected].
Say what you want a copy of or removed, and include enough to find it
— the link to a saved debate, or the email address an account
was opened with. There is no automated process behind this and no
form to fill in; it is a person reading mail.
Two things are worth knowing before you write, because they change
what can be answered.
Unpublishing does not need us
Saving a debate hands back a delete token, once.
Only a SHA-256 hash of it is stored, so possession of the token is
the only thing that can unpublish the record — reading the
database confers nothing. The token is kept in this browser under
splitbench.saved, which is why unpublishing works from
the browser that saved the debate and from no other. If you still
have that browser, you do not need to write to anyone.
Anonymous debates cannot be searched for
A saved debate records nothing about who saved it. There is no
account on it, no address, and nothing to match a person against, so
"delete everything of mine" cannot be answered by looking —
only a specific link can. That is a consequence of not asking you
for an account, and it cuts both ways.
A record whose token is lost stays until it is pushed out by the
500-record bound, unless it is removed by hand on request.