Privacy
Last updated: 8 September 2026.
Sift is run by Oxycon. This page says what the app stores about you, why it stores it, and how
to get rid of it. It describes what the code actually does; where a section says "we do not",
that is a statement about the app and not a promise about a future version.
What an account holds
Creating an account stores three things: the email address you signed up with, kept lowercased
and used to sign you in; a bcrypt hash of your password, never the password itself; and a
username made from the local part of the address. Two access tokens are generated with the
account: one for the JSON API and one for the feed addresses you paste into a reader.
If you arrived through a link carrying a ?ref= value, that value is stored on the account as
its sign-up source. It is a short label such as the name of the site that linked here, kept to
sixty-four characters.
Signing in
A sign-in creates a row in the sessions table. That row holds the account it belongs to, the IP
address the sign-in came from, the user agent string your browser sent, when it was made, and
when it was last used. The IP address and the user agent come off the request rather than from
anything you typed, and are recorded once, when the sign-in happens; the last-used time is
written again as you use the app, at most once an hour rather than on every page you open. All
three, the address, the browser string and the last-used time, are shown back to you on the
Settings screen, which lists this account's session rows with a sign-out button for each one.
Nothing else in the app reads the address or the browser string; the last-used time is also what
decides the thirty-day expiry described in the next paragraph, and it is the order that list is
shown in. What tells one signed-in browser from another inside the app is the row's own id,
which the cookie described next carries.
Two cookies come with a sign-in. The first is session_token, which this app sets itself: it
carries the signed id of that row, it is http-only and same-site lax, and it is dated far ahead so
the browser keeps you signed in. Signing out deletes both the row and the cookie, so a copy kept
elsewhere names nothing afterwards. Signing another browser out from the Settings screen deletes
its row straight away; that browser's own cookie is cleared the next time it makes a request,
which is answered as a signed-out visitor's. A row nobody has used for thirty days stops signing
anybody in the same way, and the daily prune deletes the row on its next pass, so a browser you
never sign out of does not stay signed in for good. The second is _rssgen_session, Rails' own
session cookie, which the framework sets whenever a page has a one-off message to hand to the next
page: signing up sets it, and so does being redirected to the login screen. It is encrypted with
this app's own key, it is http-only, and it lasts until the browser is closed. Reading a public
page such as this one sets neither cookie.
Those two are the only cookies this app sets. Neither is an advertising or a cross-site tracking
cookie, and nothing here follows you to another site.
What your feeds store
A source you add fetches a page on your behalf. That produces:
- the fetched document, kept as a page snapshot so a fetch can be replayed while a plan is being
repaired. Snapshots are pruned on a schedule, seven days by default, except for the newest few
of each source, which are kept so there is always something to replay. - one row per fetch recording how long it took, how many entries it found and what went wrong if
anything did. These are pruned after ninety days by default, on the same rule. - the entries themselves, as items in the collection you attached the source to. A source with no
collection stores no items at all.
Most sources read a public address, but a source does not have to. It can be given an address to
collect a token from and a token of its own, and a request header written with {{auth_token}}
has that token substituted into it when the request is built, which is how a source reads a page
that only its own credential opens. Such a token is a credential for that upstream site: it is
stored on the source as you entered it, it is never included when the JSON API describes your
sources, and it is visible and editable on that source's own settings screen. Whether you may
point the app at a page behind a login is a question for that site's terms; the Terms page says
what is allowed here.
Scoring, and what leaves this app
Scoring is done by a language model you configure yourself, with your own key and your own
endpoint. When an analyzer runs, the item's text, the item's address and the criteria you wrote
are sent to that endpoint, and the app caps how much of an item's text one prompt carries. The key
you configure is stored so the app can make that call; it is never included in an API response and
never shown to another account. The app holds no key of its own, so there is no way for your items
to be scored on somebody else's account. What the provider you chose does with that request is
governed by their terms, not by this page.
Deliveries
A delivery is the other thing that leaves this app. A Telegram report sends, on the schedule you
set, the score, the title and the address of the top items to the chat you named, through the bot
token you supplied. A Slack delivery sends, one item at a time as that item is judged, the
item's score, its title, its address and the first line of the analyzer's reasoning, to the
incoming webhook address you configured; the test button on it sends one message naming the
collection. Which items a Slack delivery sends is what you set on it: one analyzer of the
collection or none, and a score at or above which an item is posted. Both exist only because
you created them, and an account with no delivery configured sends nothing out.
The app sends one kind of mail: the link that resets a password. There is no newsletter and no
marketing mail.
Analytics
If the site is running with analytics configured, views of the landing page, of this page and of
the Terms page are counted by a self-hosted, cookie-free counter. Those three are the pages built
on the landing layout, which is the one template that loads the counter's script. The app loads
that script from the address the operator configured and passes it a site id, nothing else; it
sets no cookie of its own for it. The sign-in, the sign-up and the password screens are not
counted, and neither is anything inside the signed-in app. When either of the two analytics
settings is missing, no analytics script is loaded at all.
Who else can see it
The data of one account is not shared with another account, is not sold, and is not handed to
anybody for their own purposes. The API token reaches only the records of the account it belongs
to. The feed token opens less than the API token does, but it is not narrower than the account:
it authorises the feed, publication and analyzer feed addresses of the account that owns them, so
one of those addresses pasted somewhere public exposes the others too.
Four parties do receive part of it, and each of the four is one you chose: the model provider
you pointed an analyzer at, Telegram if you configured a report, Slack if you configured a
webhook delivery, and the site a source fetches,
which sees the request the app makes on your behalf. Beyond those, infrastructure providers
necessarily process the data in transit and at rest, the server host among them. It is handed
over otherwise only where the law requires it.
Deleting it
The Settings screen has a "Delete my account" button. It asks for the password you sign in
with, and a wrong one deletes nothing. The right one records the moment on the account, deletes
every session row of the account, and queues the deletion.
From that moment the API token is refused by the JSON API, and the feed token opens no feed, no
publication and no analyzer feed, whichever reader or script is still holding it.
What the queued deletion removes is the account row and, with it: the sources, and per source
its legacy addresses, its stored pages, its fetch records, its stored results and its extraction
plans with every version of those plans; the collections; the items in those collections, with
their assessments, their enrichment records and their source links; the item types and their
field definitions; the analyzers and their versions; the publications; the Telegram reports; and
the browser-render records. The session rows have already gone with the request. It is queued
rather than done during the request, so on a busy queue it lands minutes after the button rather
than seconds after it.
Two things stay behind afterwards, and neither is one of your records. Jobs already queued
against the deleted records stay in the queue table until a worker picks each one up; each finds
no record and finishes without doing anything. And the application log keeps one line naming the
deleted account's numeric id and how many sources, collections, items, analyzers, Telegram
reports, item types and sessions the deletion removed; that line carries no address, no name and
no item.
Write to the address behind the Contact link in the footer for a correction, or with a question
about any of this.
Contact
The Contact link in the footer of this page reaches the person who runs this app. Write there
about anything on this page. That footer is on the landing page and on both legal pages; the
sign-in, the sign-up and the password screens do not carry it.