Wall Security Architecture Архитектура безопасности Wall

How Wall protects user identity, content, payments, and platform integrity. Public, verifiable, no marketing varnish. Как Wall защищает идентичность пользователя, контент, платежи и целостность платформы. Публично, проверяемо, без маркетингового лака.

Authentication — HMAC-SHA256 against TelegramАутентификация — HMAC-SHA256 от Telegram

Wall has no passwords. Every Mini App session opens with init_data signed by Telegram's bot token. On every write endpoint, the server re-validates the HMAC-SHA256 signature against the bot's secret. If it doesn't match, the request is rejected with a generic error before reaching any business logic. Implementation in lib/telegram-auth.ts. У Wall нет паролей. Каждая сессия Mini App открывается с init_data, подписанным bot-токеном Telegram. На каждом write-эндпоинте сервер заново валидирует HMAC-SHA256 подпись по секрету бота. Если не совпадает — запрос отклоняется generic-ошибкой ещё до достижения бизнес-логики. Реализация в lib/telegram-auth.ts.

Implication: there is no email/password to leak in a database breach, no JWT secret to rotate, no OAuth third-party redirect to compromise. Your Telegram account is the only credential, and it stays inside Telegram (2FA, biometric, hardware key — whatever you've enabled there). Следствие: нет email/пароля, который мог бы утечь в БД-бричe, нет JWT-секрета для ротации, нет OAuth-редиректа к third-party для компрометации. Telegram-аккаунт — единственный credential, и он остаётся внутри Telegram (2FA, biometric, hardware key — что бы ты там не включил).

Rate limiting — Redis-backed, atomic, per-userRate-limiting — Redis-backed, atomic, per-user

Every API endpoint declares a rate limit (calls/window keyed by user ID and route). Counters live in Upstash Redis with atomic INCR + EXPIRE. Implementation in lib/rate-limit.ts. Каждый API-эндпоинт декларирует rate-limit (calls/window по userId + route). Счётчики живут в Upstash Redis с атомарным INCR + EXPIRE. Реализация в lib/rate-limit.ts.

Endpoint classКласс эндпоинта Typical limitТипичный лимит WhyЗачем
Read (feed, profile, search)Чтение (лента, профиль, поиск) 60-120 / min Generous; supports normal scrollingЩедро; поддерживает обычный скролл
Write (post, comment, reaction)Запись (пост, комментарий, реакция) 10-30 / min Stops bot-style spam without blocking real usersОстанавливает bot-spam, не блокируя живых юзеров
Sensitive (referral creation, gift send)Чувствительные (создание реф-ссылки, отправка подарка) 1-5 / hour Tight ceiling for endpoints that touch payments or affect attributionЖёсткий потолок для эндпоинтов, которые касаются платежей или атрибуции
Bot commands (/start, /share)Bot-команды (/start, /share) 10-20 / min Per-Telegram-user, prevents flood of duplicate commandsPer-Telegram-user, защищает от флуда дубликатов

Rate-limit responses are silent (no detail about which limit triggered) so attackers can't probe the surface. Repeat offenders get a tightened ceiling that scales with their report-event history. Rate-limit ответы silent (без деталей, какой лимит сработал) — атакующие не могут профилировать поверхность. Повторные нарушители получают ужесточённый потолок, масштабирующийся по их report-event истории.

Content-addressed media — SHA-256 dedup, tamper detectionContent-addressed media — SHA-256 dedup, tamper-detection

Every image, audio, video upload is hashed with SHA-256. The hash becomes the storage key. Two users uploading the identical image dedup to one stored copy. If served bytes don't match the SHA-256 hash, the request fails — tampering at storage or transport level is detectable. Каждый image, audio, video upload хэшируется SHA-256. Хэш становится storage-ключом. Два юзера, грузящих одну картинку, дедуплицируются до одной копии. Если выданные байты не сходятся с SHA-256 — запрос падает; tampering на storage или transport уровне обнаружим.

Storage backend: AWS S3 with Postgres bytea fallback for legacy content. SVG uploads use a stricter content-type check + Content-Security-Policy because of XSS risk via <script> tags inside SVG. Storage-бэкенд: AWS S3 с fallback'ом в Postgres bytea для legacy-контента. SVG-загрузки — со строгим content-type чеком и Content-Security-Policy из-за XSS-риска через <script> внутри SVG.

Input validation and sanitisationВалидация и санитизация ввода

Every user-submitted field goes through lib/validate.ts before hitting the database: Каждое user-submitted поле проходит через lib/validate.ts перед БД:

  • Length caps per field (post body 280 / 1000 for Premium; bio 200; slug 40)Лимиты длины на поле (тело поста 280 / 1000 для Premium; bio 200; slug 40)
  • Character whitelisting for slug-like fields (alphanumeric + underscore + hyphen)Whitelist символов для slug-полей (alphanumeric + _ + -)
  • URL safelist for embedded links (no javascript:, no data: for HTML payloads)Safelist URL для embedded-ссылок (нет javascript:, нет data: для HTML-нагрузок)
  • HTML stripping for free-text fields — no markup is preserved server-side, the client renders our own subsetHTML-stripping для free-text полей — markup не сохраняется server-side, клиент рендерит наш subset
  • Prisma parameterised queries prevent SQL injection by constructionPrisma parameterised queries предотвращают SQL-injection by construction

HTTP security headersHTTP security headers

Every Wall response — including the wall.tg/r/<slug> redirector and all API endpoints — sets: Каждый ответ Wall — включая wall.tg/r/<slug> redirector и все API-эндпоинты — выставляет:

X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: camera=(), microphone=(), geolocation=()
  • X-Content-Type-Options: nosniffbrowsers won't second-guess our Content-Type header (blocks MIME sniffing attacks where a server-served PNG is interpreted as JS)браузеры не будут second-guess'ить наш Content-Type (блокирует MIME-sniffing атаки, где сервер-served PNG интерпретируется как JS)
  • Referrer-Policy: strict-origin-when-cross-originwhen a user clicks an outbound link, only the origin (wall.tg) leaves Wall, not the full path. Privacy-preserving for cross-site navigation.когда юзер кликает outbound-ссылку, только origin (wall.tg) уходит за пределы Wall, не полный path. Privacy-preserving для cross-site навигации.
  • Permissions-Policyexplicitly disallow camera, microphone, geolocation. No part of Wall asks for these — denying them at the policy level is defence-in-depth.явно запретить камеру, микрофон, геолокацию. Никакая часть Wall не просит их — denial на уровне policy это defence-in-depth.

Verifiable: curl -I https://wall.tg/ shows the headers in the response. Проверяемо: curl -I https://wall.tg/ показывает заголовки в ответе.

Non-custodial financial flowsNon-custodial финансовые потоки

TON tips and Chain Post stakes go wallet-to-wallet via TON Connect, signed by your wallet directly. Wall sees the on-chain transaction (and attributes it to the right post / referral), but never holds funds. Telegram Stars are Telegram's own ledger — Wall sees the balance change after the Telegram-side payment, never custodies the Stars themselves. TON-чаевые и Chain Post-стейки идут wallet-to-wallet через TON Connect, подписываются твоим кошельком напрямую. Wall видит on-chain-транзакцию (и атрибутирует её правильному посту / рефералу), но не держит средства. Telegram Stars — собственный ledger Telegram; Wall видит изменение баланса после Telegram-side платежа, сами Stars не custody'ит.

Implication: if Wall disappeared tomorrow, your TON, Chain Posts, and wallet bindings remain on the TON blockchain. Your Stars remain in Telegram's ledger. We don't have a "Wall account balance" that could be locked or zeroed. Следствие: если Wall завтра исчезнет, твои TON, Chain Posts и привязки кошелька останутся на блокчейне TON. Твои Stars останутся в ledger Telegram. У нас нет «баланса Wall-аккаунта», который можно было бы залочить или обнулить.

Privacy — no third-party trackers, no behavioural data saleПриватность — нет third-party трекеров, нет продажи behavioral-данных

No Google Analytics, no Mixpanel, no Amplitude, no Facebook Pixel. The only telemetry is server-side application logs (PM2 stream) used by the engineering team for debugging and capacity planning — not exported, not sold, not enriched with third-party identity providers. Нет Google Analytics, нет Mixpanel, нет Amplitude, нет Facebook Pixel. Единственная телеметрия — server-side application logs (PM2 stream), используемые engineering-командой для отладки и capacity planning — не экспортируются, не продаются, не обогащаются third-party identity-провайдерами.

Referral attribution is first-party — see /ad-network for the full architecture. We attribute clicks for our own ad-buys (e.g., paid shoutouts in other Telegram channels) using a UTM-tagged parser at the bot level. No advertiser ever receives a list of users. Реферальная атрибуция — first-party, см. /ad-network. Мы атрибутируем клики наших же ad-buys (например, платных шаутаутов в чужих Telegram-каналах) UTM-тегированным парсером на уровне бота. Ни один advertiser не получает список юзеров.

Operational practicesОперационные практики

  • Two-branch flow with staging gate — every change ships to bapp.wall.lu first via dev branch auto-deploy, then to production only via main merge after verification. AI agents never touch main directly.Two-branch flow со staging-гейтом — каждое изменение сначала на bapp.wall.lu через auto-deploy dev, потом в продакшен только через merge в main после проверки. AI-агенты никогда не трогают main напрямую.
  • Hard CI gatestsc --noEmit 0 errors and npm run build green required before any commit lands. Pre-build checks (i18n strict, notifications API guard) prevent broken UI from ever reaching staging.Жёсткие CI gatestsc --noEmit 0 errors и npm run build green обязательны перед любым commit'ом. Pre-build checks (i18n strict, notifications guard) не дают сломанному UI попасть даже на staging.
  • Schema migrations are additive-only by default — new tables, new columns with defaults. DROP COLUMN / DROP TABLE require explicit owner approval. Documented in CLAUDE.md with a hard rule that automated agents never run prisma migrate reset.Schema-миграции additive-only по умолчанию — новые таблицы, новые колонки с defaults. DROP COLUMN / DROP TABLE требуют явного approval owner'а. Документировано в CLAUDE.md жёстким правилом — automated-агенты никогда не запускают prisma migrate reset.
  • Server access is read-only for AI agents — SSH allowed only for pm2 logs and pm2 status verification. No rsync, no manual builds, no service restart, no .env editing.Server-доступ для AI-агентов read-only — SSH только для pm2 logs и pm2 status верификации. Никакого rsync, никаких manual builds, никакого restart сервисов, никакого редактирования .env.
  • Secrets management via GitHub Actions secret store. Never committed to the repository. Workflow files modified only to add new env vars from secrets, never to change build/deploy structure casually.Управление секретами через GitHub Actions secret store. Никогда не коммитятся в репо. Workflow-файлы меняются только для добавления новых env vars из секретов, никогда для casual-изменения build/deploy структуры.

Reporting a security issueСообщить о security-проблеме

Use the [security] subject prefix in contact for fastest routing. Critical vulnerabilities are triaged within 24 hours. Используй [security] subject-префикс в contact для самой быстрой маршрутизации. Критические уязвимости берём в работу в течение 24 часов.

We do not currently run a public bug bounty program — we're a small team and false-positive triage cost would slow us more than it would help. We do credit responsible disclosures publicly (with researcher's permission) on the Wall blog. Мы пока не запускаем публичный bug bounty — мы маленькая команда, и стоимость triage'а false-positive'ов притормозила бы нас больше, чем помогла. Мы при этом публично кредитим responsible disclosures (с разрешения исследователя) в блоге Wall.

FAQFAQ

If Wall has no passwords, can someone log in as me by knowing my Telegram username?Если у Wall нет паролей, может ли кто-то залогиниться как я, зная мой Telegram-username?

No. Authentication uses Telegram's init_data signed by the bot token via HMAC-SHA256 (verified server-side on every write). The signature includes a timestamp and a hash that only Telegram's servers can produce — knowing your username is not enough. To impersonate you on Wall, an attacker would need to compromise your Telegram account itself, which is outside our threat model (Telegram's auth, 2FA, etc.).Нет. Аутентификация использует init_data Telegram, подписанный bot-токеном через HMAC-SHA256 (проверяется server-side на каждом write). В подпись входит timestamp и хэш, который могут произвести только сервера Telegram — знания username недостаточно. Чтобы выдать себя за тебя в Wall, атакующему нужно скомпрометировать сам Telegram-аккаунт, что вне нашей threat-модели (auth Telegram, 2FA и т.д.).

Where can I see the actual security headers Wall sends?Где увидеть реальные security-заголовки Wall?

Run curl -I https://wall.tg/, curl -I https://wall.tg/api/product, or curl -I https://wall.tg/r/test_slug. You'll see X-Content-Type-Options, Referrer-Policy, Permissions-Policy in the response. The headers are set in next.config.mjs and the request middleware — both committed in the public repo.Запусти curl -I https://wall.tg/, curl -I https://wall.tg/api/product или curl -I https://wall.tg/r/test_slug. Увидишь X-Content-Type-Options, Referrer-Policy, Permissions-Policy в ответе. Заголовки выставлены в next.config.mjs и request-middleware — оба в публичном репо.

What happens to my data if I delete my Wall profile?Что происходит с моими данными, если я удалю профиль Wall?

Your profile, posts, comments, and direct messages are deleted from our database. Chain Posts you authored remain on the TON blockchain (immutable by design — that's the whole point of chain-sealing). Media uploaded by you is deleted from S3 unless another user has reposted or referenced it via SHA-256 hash (then the bytes stay until that reference is removed too — content-addressed storage doesn't double-track).Профиль, посты, комментарии и DM удаляются из нашей БД. Chain Posts, которые ты создал, остаются на блокчейне TON (immutable by design — в этом весь смысл chain-sealing). Медиа, загруженное тобой, удаляется из S3, если на него нет ссылки от другого юзера через SHA-256 hash (тогда байты остаются, пока не уйдёт и та ссылка — content-addressed storage не double-track'ит).

Does Wall expose user emails or phone numbers anywhere?Wall экспонирует email или phone-number юзеров где-либо?

Wall doesn't collect emails or phone numbers. The only identifier we get from Telegram is the user's Telegram numeric ID and (if they made it public on their Telegram side) their @username + display name. We never request your phone number, never see your contacts, never request access to other Telegram chats.Wall не собирает email и phone-number. Единственный идентификатор, который мы получаем от Telegram — Telegram numeric ID юзера и (если он сам сделал публичным на стороне Telegram) его @username + display name. Никогда не просим телефон, не видим контактов, не запрашиваем доступ к другим Telegram-чатам.

Is the Wall codebase open source?Исходники Wall открыты?

The full repository is at github.com/gmediaorg/wall-public — public read access. Reusable libraries, infrastructure config, and architecture documentation are all visible. Some operational details (PM2 ecosystem, .env examples without secrets, deployment workflows) are also in the repo. License terms are documented per-directory.Полный репозиторий на github.com/gmediaorg/wall-public — публичный read-доступ. Reusable-библиотеки, инфраструктурный конфиг и архитектурная документация открыты. Некоторые operational-детали (PM2 ecosystem, .env примеры без секретов, deployment workflows) также в репо. Лицензионные условия документированы per-directory.

Found something concerning? Нашёл что-то тревожное?

Use [security] in your subject when contacting us — it routes to fastest triage path. Critical issues handled within 24 hours. Используй [security] в subject — это маршрутизируется в самый быстрый triage-канал. Критика — в течение 24 часов.

Contact security team Связаться с security-командой