Last updated: 30 July 2026. Applies to the Channel Janitor Slack app.
Channel Janitor scans your Slack workspace’s public channels for staleness — channels with no human activity past a threshold you configure (90 days by default) — sends a review queue of candidate channels to a Telegram chat, and archives only the channels you approve. Every action is written to an append-only audit log. To decide whether a channel is stale the app reads channel history, but the only thing it keeps from that history is a timestamp: message text is never stored. The app does not sell or share your data, and it contains no advertising or cross-site tracking.
The app’s own database holds four tables, and nothing else:
| Table | Contents |
|---|---|
Installation |
Your Slack workspace (team) ID and workspace name, the Slack bot OAuth
token (encrypted at rest with AES-256-GCM — see src/crypto.js), the bot user ID,
the Slack user ID of the person who installed the app, the OAuth scopes granted at
install, your staleness threshold in days, the plan (free / pro), the Stripe
customer and subscription IDs (only once Stripe billing is configured), the Telegram chat
ID that receives your review queue, and install/update timestamps. |
ChannelCandidate |
Per candidate channel: the channel ID and name, the timestamp of the last human
activity, the computed days-stale, the member count, the decision status
(pending / skipped / archived / error), the Telegram
chat and message IDs of the review card, and when it was proposed and decided. |
AuditLog |
Append-only: the action (install, scan, proposed, approved, skipped, archived, error, billing,
uninstall), the channel ID and name it concerned, a short free-text detail string (for example
212d stale), and a timestamp. |
LinkToken |
A random single-use token, the workspace ID it is bound to, its expiry (15 minutes after issue),
when it was redeemed, and when it was created. Created by /janitor link so that a Telegram
chat can prove which workspace it belongs to. It contains no channel data and no message content. See
the retention section for how long these rows live. |
users:read scope.| Scope | Used for |
|---|---|
channels:read | list the workspace’s public channels |
channels:history | read a channel’s history to find the timestamp of the last human message |
channels:join | join a public channel, which Slack requires before its history can be read or it can be archived |
channels:manage | archive a channel you approved |
chat:write | post messages in Slack |
commands | receive the `/janitor` slash command |
team:read | read the workspace name shown on the review cards |
This is the complete list the install URL requests. Reading channel history requires the app to be a member
of the channel, which is why channels:join is included; #general is deliberately
never joined and never scanned.
The app’s data lives in a self-hosted PostgreSQL 16 instance running in its own container
on our own server. That container is attached only to a private Docker network shared with
the app; it is not published to the internet, and the single host port it exposes is bound to
127.0.0.1 for maintenance from the server itself. The Slack bot token is additionally encrypted
at the application level with AES-256-GCM before it is written, so a database file on its own does not yield
a usable token.
Besides the database, the app writes operational log lines that the server keeps for debugging. These contain:
Logs are rotated by size rather than kept for a fixed period, so older lines are discarded as new output arrives. They are readable only by us, on the server, and are not shared with anyone.
When you uninstall the app, Slack sends an app_uninstalled event and the app
deletes your Installation row. That deletion cascades, so every
ChannelCandidate, AuditLog and LinkToken row for your
workspace is deleted with it. All four tables named above are covered; nothing about your workspace
is kept behind. The cascade is enforced by the database itself through foreign keys, not by
application code that could be skipped.
LinkToken rows whose 15-minute validity expired more than a day ago. These are the
one-time connect links produced by /janitor link; once expired they cannot be redeemed, so
removing them costs you nothing. Tokens that are still valid are never touched by that job, and it never
touches any other table.
There is no other automatic expiry or scheduled purge in the app: candidate channels and audit-log entries are kept for as long as your workspace has the app installed, and are deleted when you uninstall. Channels you archived while using the app stay archived in Slack — that is a change to your workspace, not data we hold.
The app does not sell, rent, or share your data with third parties for their own purposes. Data is exchanged only with the following providers, strictly to operate the app:
If you are located in the European Economic Area, you have the right to access, rectify or erase the personal data held about you, to restrict or object to its processing, and to data portability. You can exercise these rights by contacting us at the address below, or by uninstalling the app — which deletes your workspace’s data in full, as described under “Data retention and deletion”.
Communication with Slack, Telegram and Stripe uses HTTPS. The app authenticates to your workspace with a Slack OAuth token rather than a password, and that token is encrypted with AES-256-GCM before storage. Every inbound Slack request is verified against Slack’s request signature before it is acted on, and every Stripe webhook is verified against Stripe’s signature. Approve and skip actions are accepted only from the Telegram chat linked to your workspace.
If this policy changes, the updated version is published on this page with a new “last updated” date.
Questions about this policy: info@kyc-checks.nl.