[READ-ONLY] Mirror of https://github.com/thoda-dev/shhh. Self-hostable zero-knowledge pastebin for secrets that expire on their own
docker end-to-end-encryption nuxt nuxtjs pastebin secrets selft-hosted zero-knowledge
shhh apps docs content 2.self-hosting 4.administration.md
6.3 kB
Markdown


title: Administration description: Accounts, invitations, IP lists and day-to-day operation. navigation: icon: i-lucide-users #

The admin dashboard lives under /admin and is reachable from your own dashboard once your account is an admin or super admin.

Accounts and roles #

/admin/users lists every account with its role and 2FA status. Any admin can see the list; what they may do depends on their role — an admin acts on plain users only, a super admin acts on anyone but themselves.

Role changes are super-admin only. Every action is written to the audit log.

Inviting people #

/admin/invitations sends an invitation to an email address. An invitation works even when public registration is closed — that is its purpose.

  • The token is single-use and expires after invitation_expiry_days (7 by default).
  • The invited address is fixed by the invitation. The account is created on that address and no other, and arrives already verified since the link only reached that mailbox.
  • The role is always user. To make somebody an admin, invite them, then change their role — two deliberate actions rather than one shortcut.
  • Revoking a pending invitation kills the link immediately.

::note Invitations need a mail provider. With MAIL_PROVIDER=none the form is replaced by a notice, since there would be no way to deliver the link. ::

A closed instance is therefore registration_enabled off plus invitations for the people you want.

IP allowlist and blocklist #

The instance bans IPs automatically when they request known probe paths or identify as untrusted bots. Two screens let you manage the result:

  • /admin/allowed-ips — an allowlist that bypasses the whole security middleware. Use it for your own monitoring or office IP if it ever gets caught.
  • /admin/banned-ips — the list of bans, automatic and manual. You can add a ban yourself with a reason and an optional expiry; leaving the expiry empty makes it permanent.

Automatic bans expire after AUTO_BAN_DURATION_HOURS, 72 by default — long enough to discourage a scanner, short enough that somebody caught by mistake gets back in without you noticing. Set it to 0 to make them permanent. Bans you place by hand are never shortened by that setting.

An allowlist entry wins over a ban, and removing it re-applies the ban on the next request.

::warning Command-line HTTP clients with their default user agent are identified as untrusted bots and banned. If you script against your instance, allowlist its address or send a real browser user agent — /api/health is the only path exempt from this. ::

Settings #

/admin/settings holds every operational limit. See Configuration for what each one does. Changes take effect immediately, with no restart.

Storage #

/admin/storage is super admin only. It reads live from the pastes table — not the hourly denormalised counters — and shows usage as progress bars against the quotas you set in the settings:

  • Instance quotas — bytes stored against max_total_storage_bytes, pastes against max_total_pastes. A quota left empty shows as "no quota set" instead of a bar, since there is nothing to fill. These are the two caps that make creation fail with a 507 or a 503.
  • What is using it — the same total split by text/file and anonymous/authenticated, so a surprise is attributable before you change a limit.
  • Expired or fully read — pastes that are already unreadable, because they expired or were read as many times as allowed, and that the hourly purge has not deleted yet. Subtract it before concluding the instance is full.
  • Largest paste against the per-paste limits — the biggest stored payload against max_text_size_bytes and max_upload_size_bytes. A bar near empty means the ceiling is set far above what anybody actually sends.
  • Top accounts by storage — the ten heaviest accounts.
  • On disk — pg_database_size and the size of the pastes table with its indexes and TOAST. Always larger than the sum of the ciphertexts, and the number your volume actually runs out of. Some managed PostgreSQL providers deny these functions, in which case the card says so rather than reporting zero.

Purge now on that card runs the hourly task on demand, against the same predicate, so it deletes exactly what the card announces. Use it when you need the space back before the next hour rather than waiting for the schedule.

Deleting somebody's pastes #

The ⋮ menu on each row of /admin/users carries the destructive actions, each behind a confirmation naming the account and the exact number of pastes involved:

  • Delete expired and fully read pastes — the account's share of what the purge would take anyway.
  • Delete all pastes — everything the account holds, readable ones included. The account itself is kept.
  • Delete account — unchanged, still an admin action on plain users and a super admin action on anyone else.

The two paste actions are super admin only. An admin who may remove an account already cascades its pastes; one who may not has no business emptying it either.

::note These act on counts, never on content. The server cannot read a paste, so an operator deleting one is acting on an external report — a takedown notice, an abuse complaint — not on their own inspection. There is deliberately no way to browse pastes: the tools work per account and in bulk. ::

Every one of them lands in the audit log with the actor, the target and the number deleted.

Maintenance #

An hourly scheduled task deletes pastes that have expired or exhausted their read count, then recomputes the denormalised statistics. Expiry itself is enforced in real time on every read, so the task's cadence only affects when rows are reclaimed, never whether a dead paste is still readable.

Nothing else needs a cron job.

Audit log #

Administrative actions — settings changes, role changes, deletions, invitations, IP list changes — are recorded with the actor, the target and a JSON payload. Deleting an account anonymises its entries rather than removing them: the action stays on record, the actor does not.

There is no UI for the log yet; query the admin_audit_log table directly.