Security

Safe, private, and verifiable.

Every claim on this page is something we can show you — not just tell you. Facts, dates and named things, for the person filling in the questionnaire.

Illustration: the rii.link routing core sealed behind its security boundary — TLS in front, abuse checks, the tenant-partitioned database, EU residency in Frankfurt, and the append-only audit log.

The request path

Five hops. Each one sealed.

Not the data path — the security boundary. See how a click becomes a dashboard row on /analytics; here's what happens to the request itself, hop by hop.

TLS handshakesealed · tls 1.2+ · 1.3 preferred

Every request negotiates TLS 1.2 or newer — 1.3 preferred — before anything else happens. Plain HTTP serves exactly one thing: a redirect to HTTPS.

tls_version: 1.3 · cipher: TLS_AES_128_GCM_SHA256 · hsts: max-age=63072000; includeSubDomains
Abuse checksealed · honeypot · timing · pow

Write endpoints and public forms pass named bot checks — honeypot fields, render-to-submit timing, proof-of-work on suspicious traffic. The redirect itself skips all of it and stays fast.

# api writes & public forms only — never the redirect
honeypot: empty · render→submit: 1.2 s · pow: nonce accepted
Lookupsealed · o(1) · read-only

The slug resolves with a single O(1) read from an in-memory replica. The redirect path holds no write credentials — a compromised edge node couldn't alter a single link.

GET slug:acme-launch301 https://acme.com/launch (0.4 ms · replica · read-only)
Tenant boundarysealed · tenant_id on every row

Every row carries a tenant_id, and every query is scoped to it at the repository layer. Crossing tenants isn't forbidden by policy — it's architecturally impossible to express.

SELECT dest FROM links WHERE slug = $1 AND tenant_id = $2 — generated by the repository layer, not optional
Responsesealed · logged vs discarded

The 301 goes back in milliseconds. What we keep is aggregated and anonymous; what identifies you is dropped at the edge.

kept →
utc timestamp
country = DE
device = mobile
dropped →
raw ip, after geo lookup
fingerprints — never built
third-party cookies — none

abuse checks never touch the redirect hot path — the click stays at 11 ms ↓

p50 redirect latencymeasured from region
Frankfurt11 ms · primary
Amsterdam16 ms
London19 ms
Virginia88 ms
Singapore152 ms
Infrastructure & availability

Boring, on purpose.

Anycast edge, EU-resident storage, and a public status page — availability is a security property here, not a marketing one.

99.99% monthly uptime target — the Enterprise SLA puts credits behind it
Anycast network — the nearest edge answers, Frankfurt stays the system of record
Public status page with 90-day history — see live status at status.rii.link
Data protection

Where every byte lives.

Residency per data type — not a blanket claim — plus the encryption and retention facts a questionnaire actually asks for.

Residency — per data type
Routing databaseFrankfurt, DE

Slugs, destinations, rules — the system of record.

Analytics eventsFrankfurt, DE

Aggregated counters — country and device class only.

Object storages3 · eu-central-1

QR exports and uploads on AWS S3.

no us mirror · backups stay in region

Encryption & retention — spec sheet
In transitTLS 1.2+ enforced, 1.3 preferred · HSTS on all rii.link hosts
At restAES-256 on database volumes and backupsprovider-verified — confirm
Click eventsCountry + device class derived at the edge; raw IP discarded after geo lookup
Deleted linksPurged from replicas and backups within 30 days
Audit logRetained 2 years on Enterprise — same figure as the contract

We do not sell click data. Not aggregated, not “anonymized”, not ever.

Application security

The parts your pentester greps for.

One fact per line — the implementation detail lives on /developers.

rii/app — security facts
authshort-lived JWTs, scoped claims, rotation built indocs →
data layerevery query ships WHERE tenant_id = $ctx — enforced by the repository, not by reviewdocs →
webhooksHMAC-SHA256 signature + timestamp header on every deliverydocs →
# no long-lived api passwords · replay-proof by default
Abuse & link safety

What’s real, what’s roadmap.

Named mechanisms only. Where something isn’t live yet, it says so — this page never implies.

Bot protectionHoneypot fields, submission timestamps, proof-of-work on suspicious traffic — on API writes and public forms.Live
Rate limitingPer-key and per-IP on the API — limits documented on /developers.Live
Malicious-URL scanningDestination scanning at creation time. Not live yet — and this page won't imply it is.Roadmap
Compliance & certifications

Status, not spin.

In progress means in progress — never hidden, never dressed up as certified.

SOC 2 Type IIIn progress

Observation window underway. In progress ≠ certified — the report gets published here the day it exists, not before.

DPA — GDPR / DSGVOVia sales

Standard template with processor terms, TOMs and the subprocessor list — request from sales@rii.link, countersigned in days.

Subprocessors — the whole list
AWS S3Object storage — QR exports, uploadseu-central-1 · Frankfurt
SendGridTransactional email — receipts, alertsUS · SCCs in the DPA
MaxMind †IP-to-country database, self-hostedqueried locally, EU

† maxmind geolite2 is queried locally — ip addresses never leave our infrastructure. change notice: 30 days by email.

Responsible disclosure

security.txt, published.

RFC 9116, at the well-known path — so a researcher who finds something reaches a human in minutes, not a contact form.

Reports acknowledged within 72 hours — by an engineer, not an autoresponder
Good-faith research is safe here — no legal threats for responsible reports
rii.link/.well-known/security.txt
Contact: mailto:security@rii.link
Expires: 2027-08-09T00:00:00.000Z
Encryption: https://rii.link/.well-known/pgp-key.txt
Preferred-Languages: en, de
Canonical: https://rii.link/.well-known/security.txt
Policy: https://rii.link/security#disclosure
# this preview is the spec — the real file ships with the page
Talk to us

Mid-security-review?

Send the questionnaire — the engineers who built the request path answer it, with the DPA and subprocessor list ahead of the call.

SLA, migration and rollout live on /solutions/enterprise — same figures, same contract.