Skip to content
Security

What Plikli does to keep sites safe

This page lists only measures built into Plikli today, described by what they do.

Keeping sites apart

Database row-level security

Tables holding site data use PostgreSQL row-level security (FORCE RLS), so one site’s rows can’t be read or written from another.

Checked in the API too

The API refuses requests for sites the signed-in user has no role on.

Sign-in and accounts

How passwords are stored

Passwords are hashed with scrypt and a random salt. They must be at least 10 characters.

Two-step sign-in

Authenticator apps (TOTP), plus 10 single-use backup codes stored as hashes. Each user can turn it on.

Sessions

Every request is checked against a server-side session. Signing out ends it at once, sessions expire after 7 days, and a password reset signs out every device. Roles are re-read on each request.

Rate limits

Sign-in, two-step codes, sign-up and password resets are limited to 10 tries per 15 minutes per IP address. The API, comments and contact forms have limits too.

No account lookups

The password-reset screen doesn’t reveal whether an email address has an account.

Roles and records

Six roles

Owner, administrator, editor, author (own posts only), contributor (drafts only) and viewer, set per site.

Audit log

Changes to content, media, settings, users and roles, domains, keys and exports are recorded.

Secrets

Encrypted at rest

Google and Shopify tokens, two-step secrets and WordPress import credentials are encrypted with AES-256-GCM. Import credentials are deleted when the import ends.

API keys stored as hashes

API keys and app passwords are stored only as SHA-256 hashes, shown once and revocable. One-time tokens are hashed and expire.

No default secrets

In production the app refuses to start with default secrets.

Content and files

HTML allowlist

Post HTML is cleaned against an allowlist both when saved and when shown: scripts, iframes in rich text and event handlers are removed, and only http, https, mailto and tel links are kept.

Checked uploads

Image types are detected from the file’s bytes, not its name, and SVG is refused. Images are served with nosniff and a sandbox CSP.

No plugin code runs

An uploaded WordPress plugin zip is only read for its header (up to 10 MB). Its code is never run or stored.

Connections

Admin CSP and HSTS

The admin sends a strict Content-Security-Policy (own scripts only, no framing by other sites), and the admin and API send HSTS.

Baseline headers on sites

Every public site sends nosniff, Referrer-Policy, X-Frame-Options (SAMEORIGIN) and Permissions-Policy.

Safe outbound fetching

When the server fetches outside pages (imports, blog cards, link checks), it connects only to public addresses, re-checks every redirect, and caps size and time.

Signed webhooks

Stripe and Shopify webhooks are processed only after their signatures are verified.

Readers’ privacy

Consent banner

Off, EU readers only, or everyone. Until the reader accepts, Google Analytics, Tag Manager, Google Ads, AdSense, embedded scripts and iframes and custom code are held back. Plikli itself sets no cookies.

External-transmission notice

A page (/third-party-services) listing each service, recipient, data and purpose, for Japan’s external-transmission rules. GA4, GTM, Google Ads, AdSense and a theme’s Google Fonts web font are added automatically, and owners can add more.

Privacy policy template

A template written for Japan’s APPI, with GDPR notes. Fill in the 【】 parts; it is not legal advice.

Data we don’t collect

Page views are kept as a daily count per post with no IP address stored (an IP is held in memory for 30 minutes to remove repeats). Comments and contact messages store no IP address either.

Backup frequency, retention and where data is stored are not covered on this page. Ask us if you need them. Contact us
Get started

Start by moving one site.

New sites get 14 days of Pro for free, no card needed. The WordPress import shows you a preview before it writes anything.