SecurityControls that exist,
Controls that exist,
not intentions.
Every line below describes something the code does today. When something changes, the line is removed, not reworded. Last checked against the code: October 22, 2026.
No passwords stored, ever CSRF token on every write TLS with HSTS preload Rate limits on every endpoint that writes
Authentication
- Sign-in is Discord OAuth2 with the
identifyscope only. Linky never receives an email address, server list or messages, and there is no password to store or leak. - The OAuth flow uses a random
statevalue checked on return. Sign-in attempts are rate limited per address. - Sessions use a cookie flagged
HttpOnly,SecureandSameSite=Lax, with strict session IDs and regeneration on sign-in. - API keys for AI apps are random 160-bit values, stored only as SHA-256 hashes, shown once, revocable in one click.
Writes
- Every request that changes data requires a per-session CSRF token in a header.
- Every field is validated and bounded on the server: lengths, enums, colors, URLs (no
javascript:,data:or file schemes), timezones, numbers. - Embeds are only generated for known media hosts; pasted
<iframe>code is accepted from an allow-list of 30 hosts and rewritten, never echoed. - Uploads are checked by MIME sniffing on the server, size-limited per type, re-encoded (images to WebP via GD, video to WebM via FFmpeg) so original bytes are never served, and stored under fixed names so a file can never be a script.
- Rate limits apply to saves, uploads, handle checks and claims, imports, metadata fetches, votes, contact messages, view and click counting, and the MCP endpoint (60 calls a minute). Redis backs the limits with a file fallback.
Visitors
- Public pages set no cookies and load no third-party scripts. Icons are inline SVG. Fonts you pick beyond the three bundled ones load from Google Fonts; that is the only outside request a profile page makes.
- Analytics hashes the visitor’s address and browser with a salt that rotates daily, then discards the original. Poll voters are stored as a peppered hash so a vote can be changed but never traced.
- Live cards (GitHub, Discord, Roblox, Minecraft, YouTube, weather) are fetched by Linky’s server with timeouts and cached; visitors never contact those services and no keys exist to expose.
- Server-side fetches (imports, link metadata) refuse private and loopback addresses.
Transport and headers
- HTTPS everywhere with HSTS (one year, preload). HTTP and www redirect permanently.
X-Content-Type-Options: nosniff,X-Frame-Options: SAMEORIGIN,Referrer-Policy: strict-origin-when-cross-originand a restrictivePermissions-Policyon every response.- Application source, configuration, user configs and analytics files are blocked at the web server; only media files under a user folder are reachable, by exact name pattern.
Operations
- One server, operated by one person. Dependencies are pinned (Composer and npm lockfiles) and builds are fresh from them.
- A security log records sign-ins, saves, renames, uploads, key creation and deletions with IP addresses, kept for abuse prevention.
- Deleting your account removes your directory immediately: configuration, media, analytics and inbox.
Found a vulnerability?
Write to [email protected] with “security” in the subject. It reaches a person, not a queue, and real reports earn the Bug hunter badge. There is no bounty programme. Good-faith research is welcome if you test only your own account, access no more data than needed and keep none of it, do not degrade the service, and report promptly and privately.
What Linky does not have yet, honestly: two-factor authentication on top of Discord (Discord’s own 2FA protects the sign-in), and an external uptime monitor; the status page checks live when you open it.