Security architecture
How Wall handles authentication, rate limiting, payment custody, content integrity, and what we deliberately don't do (no third-party trackers, no passwords, no custodial TON). Each claim has a verification path; report security issues via wall.support/contact with [security] prefix (24-hour critical SLA).
- HMAC-SHA256 Telegram authentication on every write endpoint — no passwords stored
- Redis-backed atomic rate limiting per-user / per-route — no race conditions
- Content-addressed media (SHA-256 dedup) — tamper-evident
- Non-custodial TON via TON Connect — Wall holds zero user funds for TON-rail flows
- No third-party analytics — no GA, no Pixel, no Mixpanel, no session-replay
- Additive-only schema migrations — data isn't silently restructured between releases
- Two-branch staging-gated CI — no manual production releases
Authentication — HMAC-SHA256 against Telegram
Every write endpoint in Wall verifies the request originated from a real Telegram client. Telegram signs the user's session payload (init_data) with a secret derived from the bot token; Wall recomputes the HMAC-SHA256 server-side and rejects mismatches.
What we never store: passwords, password hashes, JWTs, refresh tokens, recovery questions. There is no auth secret on our side that can leak. Compromise of our database does not produce credentials usable elsewhere.
Trust boundary: Wall trusts Telegram's identity layer. If Telegram itself is compromised, that compromise propagates to Wall. We don't pretend otherwise; the trust model is documented and inherits Telegram's security envelope.
Verify
The lib/telegram-auth.ts module documents the exact HMAC chain. Every public write endpoint in app/api/ calls getUser() first; getUser() rejects unauthenticated requests with 401 before any business logic runs.
Rate limiting — Redis-backed, atomic, per-user
Every endpoint that mutates state is rate-limited. Limits are enforced atomically using Upstash Redis INCR + EXPIRE — no race conditions, no time-of-check / time-of-use bugs.
- Tier-aware limits (Free / Premium / Ultra) for AI @mentions, Pixel Battle cooldown, posting velocity
- Per-IP global limits for unauthenticated probing surfaces (rejected before reaching authenticated handlers)
- Honeypot middleware (
middleware.ts) tarpits scanner traffic with 200–600ms randomized delay and emits structured[honeypot] HITlogs for downstream IP-reputation accumulation - Body-size DoS guard at the edge — rejects oversized POST/PUT/PATCH bodies (1 MiB default, 250 MiB for the two routes that legitimately need it:
POST /api/postsfor inline graffiti data-URLs,/api/media/uploadmultipart)
Content-addressed media — SHA-256 dedup, tamper detection
Every uploaded image / video is hashed (SHA-256) on receipt. The hash is the storage key — duplicate uploads collapse to a single object, and tampering is immediately detectable (the URL doesn't change, but the served bytes wouldn't hash to the URL key).
For Chain Posts, the media hash is one input to the on-chain anchor transaction — making tampering provable on the TON blockchain in addition to detectable client-side.
Non-custodial financial flows
TON-rail flows are non-custodial. TON tips, TON post donations, and Chain Post seals all go through TON Connect — the user's wallet (Tonkeeper, MyTonWallet) signs every transaction, and Wall never holds the funds. Wall is not in the path; we cannot move user TON unilaterally.
Stars-rail flows go through Telegram. Subscriptions, gifts, paid posts use Telegram Stars (XTR). Telegram processes the payment (App Store / Google Play / Telegram's card processing) and we receive a portion via Telegram's payout mechanism. We never see your card; Apple, Google, or Telegram's processor does.
What we never have: your TON private key, your seed phrase, your wallet-level approvals (other apps' transactions), your card number.
HTTP security headers
Defence-in-depth at the response layer:
X-Content-Type-Options: nosniff— browsers don't second-guess our Content-Type, blocks MIME-sniffing attacksReferrer-Policy: strict-origin-when-cross-origin— outbound clicks expose only the origin (wall.tg), not the full pathPermissions-Policy— explicitly denies camera, microphone, geolocation. Wall doesn't need them; denying at policy level is structural
Privacy — no third-party trackers, no behavioural data sale
Zero third-party analytics. No Google Analytics, no Meta Pixel, no Mixpanel, no Hotjar, no Sentry session-replay, no PostHog, no Amplitude. First-party attribution only.
No behavioral data sale. Reading patterns, dwell time, scroll depth, click data — kept first-party for product reasons (recommendation feeds, abuse detection) and not transferred to any third party. We don't have an ad-targeting business model that would create the temptation; the Wall Ad Network (wall.support/ad-network) uses UTM-tagged campaign URLs, not behavioural targeting.
What we collect (because the product needs it): your Telegram identity (provided by Telegram on connect), the posts / comments / reactions you create, the wallets you connect (TON address, read-only), payment records for accounting, and abuse-related metadata (rate-limit counters, ban records). All documented in /privacy.
Operational practices
- Two-branch staging-gated CI — push to
devdeploys tobapp.wall.lu(staging). Merge tomaindeploys toapp.wall.lu(production). Direct push to main is forbidden. - Additive-only migrations — DROP COLUMN / DROP TABLE require explicit owner approval and are marked with
-- WARNING: drops <column/table>as the first line. - History sacred — no force-push, no rebase, no amend on pushed commits. Audit-trail integrity via the
mainbranch's merge-commit history. - AI commit attribution — every AI-agent commit is signed under the agent's identity (Co-Authored-By: agent name) in the public source mirror at github.com/gmediaorg/wall-public.
Report a security issue
Use wall.support/contact with subject prefix [security]. Critical issues get a 24-hour SLA — the security alias routes directly to a human.
We follow responsible disclosure: name reporters publicly (with their consent) on this page after the fix lands. Bounties: not formalized yet — we work with reporters case-by-case.
Common questions
Where can I report a security issue?
Use the [security] subject prefix in wall.support/contact. Critical issues get a 24-hour SLA — the security alias routes directly to a human. We honor responsible disclosure: name reporters publicly (with their consent) on this page after the fix lands.
Are passwords ever stored?
No. Wall uses HMAC-SHA256 over Telegram's signed init_data on every write endpoint. The signature is verified server-side using the bot token; if it matches, the request is authenticated. There is no password file, no JWT, no session secret on our end that can leak.
Can Wall drain my TON wallet?
No. TON tips, post donations, and Chain Post seals all flow through TON Connect — every transaction requires explicit approval inside your wallet app (Tonkeeper, MyTonWallet). Wall never sees your private key or seed phrase. The worst-case if Wall is compromised: an attacker could try to trick users into approving malicious transactions, which the wallet still gates on user confirmation.
Are there third-party trackers on Wall?
No. No Google Analytics, no Meta Pixel, no Mixpanel, no Hotjar, no Sentry session-replay. First-party attribution only — referral tracking and ad-network UTMs are computed in our own infrastructure. Reader behavior data is not sold or shared.
How are rate limits enforced?
Redis-backed atomic counters per (user, route) pair. Limits scale with subscription tier (Free / Premium / Ultra) for AI-mention quota and Pixel Battle cooldown; abuse-grade limits (per-IP for unauthenticated probing) are global. The honeypot middleware separately tarpits scanner traffic and emits structured logs for IP-reputation accumulation.
Is the source code auditable?
The public source mirror at github.com/gmediaorg/wall-public covers docs, canonical-asset HTML, and the open-data archive (CC BY 4.0). The live application source stays private — the mirror is the audit surface for documentation and structural claims. Security-architecture verifiability comes from running the curl examples documented on this page against the live app.
What happens during a CI deploy?
Push to dev → GitHub Actions builds + deploys to bapp.wall.lu (staging). Merge to main → builds + deploys to app.wall.lu (production). No manual production releases. Schema migrations are additive-only by default; DROP requires explicit owner approval. Build worker has read-only repo access; production secrets live in PM2 env (separate from CI).