Wrapped
Анонимный сервис одноразовой передачи зашифрованных данных (текст, изображения, файлы). Шифрование выполняется в браузере (Web Crypto), на сервере хранится только ciphertext. После первого успешного открытия пакет удаляется из хранилища.
Возможности:
- zero-knowledge шифрование на клиенте
- одноразовое открытие (unwrap)
- опциональный пароль (
client_onlyилиserver_gate) - CAPTCHA: Cloudflare Turnstile и/или hCaptcha
- админка с лимитами, TTL, allowlist MIME, audit-логом
- Docker Compose и Helm
Ссылка для получателя: /w/<id>#<key> или токен wrapped_v1.<id>.<key>. Ключ шифрования в URL-фрагменте (#...) на сервер не уходит.
Скриншоты
Интерфейс на русском и английском, светлая и тёмная тема.
Создание wrap
| Светлая тема | Тёмная тема |
|---|---|
![]() |
![]() |
Создание с текстом и файлами
Текст с подсветкой синтаксиса, вложения (изображения, документы), TTL и опциональный пароль.
| Светлая тема | Тёмная тема |
|---|---|
![]() |
![]() |
Токен выдан
После создания — ссылка /w/<id>#<key> и wrapped_v1-токен. Ключ только во фрагменте URL.
| Светлая тема | Тёмная тема |
|---|---|
![]() |
![]() |
Unwrap (открытие)
Одноразовое открытие: ciphertext удаляется на сервере, превью — только в текущей сессии браузера.
| Светлая тема | Тёмная тема |
|---|---|
![]() |
![]() |
Быстрый старт (разработка)
Нужны Docker и Docker Compose.
make env # создаёт .env из .env.example
make up # app + Postgres + MinIO + nginx
| Сервис | URL |
|---|---|
| Приложение | http://localhost:8000 |
| Админка | http://localhost:8000/admin |
| MinIO Console | http://localhost:9001 (wrappedminio / wrappedminio123) |
| Postgres (опционально с хоста) | 127.0.0.1:5433 |
make logs # логи app
make down # остановить
make clean # остановить и удалить volumes
Локальный docker-compose.yml собирает образ из Dockerfile и монтирует код с --reload — это режим разработки. Для сервера используйте готовый образ с Docker Hub (см. ниже).
Запуск на сервере (Docker Hub + Compose)
Образ приложения публикуется на Docker Hub. На сервере его не нужно собирать — достаточно docker compose pull / up.
1. Подготовка
Создайте каталог и файлы:
mkdir -p /opt/wrapped && cd /opt/wrapped
Создайте .env (обязательно смените секреты):
# Приложение
APP_NAME=Wrapped
APP_ENV=production
APP_SECRET_KEY=замените-на-длинную-случайную-строку
APP_BASE_URL=https://wrapped.example.com
LOG_LEVEL=INFO
DOCS_ENABLED=false
# Админ
ADMIN_USERNAME=admin
ADMIN_PASSWORD=замените-пароль
# БД и S3 задаются в compose (см. ниже). Для внешнего Postgres/MinIO
# переопределите DATABASE_URL и S3_* здесь или в environment сервиса app.
# CAPTCHA (опционально; site keys также можно задать в админке)
TURNSTILE_SECRET_KEY=
HCAPTCHA_SECRET_KEY=
# Доверие к прокси для реального IP клиента
TRUSTED_PROXIES=127.0.0.1,::1,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
APP_PORT=8000
2. docker-compose.yml для продакшена
Образ на Docker Hub: inecs/wrapped (тег задайте нужный, например 0.1.0).
services:
proxy:
image: nginx:1.27-alpine
container_name: wrapped-proxy
restart: unless-stopped
ports:
- "${APP_PORT:-8000}:80"
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
depends_on:
- app
app:
# Образ с Docker Hub — не build:
image: inecs/wrapped:0.1.0
container_name: wrapped-app
restart: unless-stopped
expose:
- "8000"
env_file:
- .env
environment:
DATABASE_URL: postgresql+asyncpg://wrapped:CHANGE_DB_PASSWORD@postgres:5432/wrapped
S3_ENDPOINT_URL: http://minio:9000
S3_ACCESS_KEY: CHANGE_MINIO_USER
S3_SECRET_KEY: CHANGE_MINIO_PASSWORD
S3_BUCKET: wrapped
S3_USE_SSL: "false"
S3_CREATE_BUCKET: "true"
APP_BASE_URL: ${APP_BASE_URL:-http://localhost:8000}
APP_ENV: production
DOCS_ENABLED: "false"
TRUSTED_PROXIES: ${TRUSTED_PROXIES:-127.0.0.1,::1,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16}
depends_on:
postgres:
condition: service_healthy
minio:
condition: service_started
minio-init:
condition: service_completed_successfully
# Миграции и старт уже в CMD образа:
# alembic upgrade head && uvicorn ...
postgres:
image: postgres:16-alpine
container_name: wrapped-postgres
restart: unless-stopped
environment:
POSTGRES_USER: wrapped
POSTGRES_PASSWORD: CHANGE_DB_PASSWORD
POSTGRES_DB: wrapped
volumes:
- wrapped_pg_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U wrapped -d wrapped"]
interval: 5s
timeout: 5s
retries: 10
minio:
image: minio/minio:latest
container_name: wrapped-minio
restart: unless-stopped
command: server /data --console-address ":9001"
environment:
MINIO_ROOT_USER: CHANGE_MINIO_USER
MINIO_ROOT_PASSWORD: CHANGE_MINIO_PASSWORD
volumes:
- wrapped_minio_data:/data
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
interval: 10s
timeout: 5s
retries: 5
minio-init:
image: minio/mc:latest
container_name: wrapped-minio-init
depends_on:
minio:
condition: service_healthy
entrypoint: >
/bin/sh -c "
mc alias set local http://minio:9000 CHANGE_MINIO_USER CHANGE_MINIO_PASSWORD &&
mc mb --ignore-existing local/wrapped &&
exit 0
"
volumes:
wrapped_pg_data:
wrapped_minio_data:
Рядом положите nginx.conf (прокси на app):
server {
listen 80;
server_name _;
client_max_body_size 64m;
location / {
proxy_pass http://app:8000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
proxy_read_timeout 120s;
}
}
Готовый пример конфига также есть в репозитории: deploy/nginx.conf.
3. Запуск
docker compose pull
docker compose up -d
docker compose ps
curl -fsS http://127.0.0.1:8000/health
Обновление на новую версию образа:
# в .env / compose поменяйте тег, например inecs/wrapped:0.2.0
docker compose pull app
docker compose up -d app
HTTPS лучше завернуть снаружи (Caddy, Traefik, nginx на хосте, Cloudflare Tunnel) и проксировать на ${APP_PORT}. В APP_BASE_URL укажите публичный https://... URL.
Внешний Postgres и S3/MinIO
Если БД и объектное хранилище уже есть, уберите сервисы postgres, minio, minio-init из compose и задайте:
| Переменная | Пример |
|---|---|
DATABASE_URL |
postgresql+asyncpg://user:pass@db-host:5432/wrapped |
S3_ENDPOINT_URL |
https://s3.example.com |
S3_ACCESS_KEY / S3_SECRET_KEY |
ключи |
S3_BUCKET |
wrapped |
S3_USE_SSL |
true |
S3_CREATE_BUCKET |
false (если бакет уже создан) |
S3_REGION |
us-east-1 |
Минимальный compose в этом случае — только app (+ опционально proxy):
services:
app:
image: inecs/wrapped:0.1.0
restart: unless-stopped
ports:
- "8000:8000"
env_file:
- .env
environment:
APP_ENV: production
DOCS_ENABLED: "false"
Переменные окружения
| Переменная | Описание | По умолчанию |
|---|---|---|
APP_NAME |
Имя сервиса | Wrapped |
APP_ENV |
development / production |
development |
APP_SECRET_KEY |
Секрет сессий/подписей | — смените |
APP_BASE_URL |
Публичный URL (ссылки, редиректы) | http://localhost:8000 |
DOCS_ENABLED |
/docs, /redoc, /openapi.json |
в prod лучше false |
LOG_LEVEL |
Уровень логов | INFO |
ADMIN_USERNAME |
Логин админки | admin |
ADMIN_PASSWORD |
Пароль админки | — смените |
DATABASE_URL |
Postgres (asyncpg) |
— |
S3_ENDPOINT_URL |
Endpoint MinIO/S3 | — |
S3_ACCESS_KEY |
Access key | — |
S3_SECRET_KEY |
Secret key | — |
S3_BUCKET |
Имя бакета | wrapped |
S3_REGION |
Регион | us-east-1 |
S3_USE_SSL |
TLS к S3 | false |
S3_CREATE_BUCKET |
Создавать бакет при старте | true |
TURNSTILE_SECRET_KEY |
Секрет Turnstile | пусто |
HCAPTCHA_SECRET_KEY |
Секрет hCaptcha | пусто |
TRUSTED_PROXIES |
IP/CIDR прокси для X-Forwarded-* |
loopback + RFC1918 |
Полный шаблон: .env.example.
Модель безопасности
- Zero-knowledge. Шифрование AES-GCM в браузере. Сервер видит только ciphertext и метаданные (TTL, MIME, размер).
- Одноразовое открытие. После успешного unwrap объект удаляется из S3/MinIO, статус wrap →
consumed. - Пароль. Режим
client_only— пароль участвует в ключе на клиенте;server_gate— сервер проверяет хеш (Argon2) до выдачи ciphertext. - CAPTCHA. Включается в админке; секреты — через env, site keys — в UI.
- IP в audit. Реальный IP берётся из заголовков только от
TRUSTED_PROXIES.
Ключ в #fragment не отправляется на сервер в HTTP-запросе страницы.
Админка
- UI:
/admin(логин/admin/login) - Настройки: лимиты загрузки, TTL, rate limit, MIME allowlist, CAPTCHA, режим пароля, retention audit
- Audit-лог и опасные операции (purge) — в соответствующих разделах UI
- JSON Admin API: Basic Auth (
ADMIN_USERNAME/ADMIN_PASSWORD), тег OpenAPIAdmin
API (автоматизация)
Тот же ZK-протокол, что и в UI: сначала шифруете локально, затем:
| Метод | Путь | Назначение |
|---|---|---|
GET |
/api/v1/settings |
Публичные лимиты, CAPTCHA, режим пароля |
POST |
/api/v1/wraps |
Загрузить ciphertext + метаданные |
POST |
/api/v1/wraps/{id}/unwrap |
Одноразово получить ciphertext |
GET |
/health |
Healthcheck |
Серверного endpoint «зашифруй за меня» нет.
При DOCS_ENABLED=true доступны Swagger /docs и OpenAPI /openapi.json.
Сборка и публикация образа
Нужен логин в Docker Hub: docker login (пользователь inecs).
# Сборка + push на hub.docker.com как inecs/wrapped:0.1.0
make release IMAGE_TAG=0.1.0
# Только собрать и затегать локально (без push)
make release IMAGE_TAG=0.1.0 PUSH=0
По умолчанию: RELEASE_REGISTRY=inecs, IMAGE_TAG=0.1.0 → образ inecs/wrapped:0.1.0.
Эквивалент вручную:
docker build -t inecs/wrapped:0.1.0 .
docker push inecs/wrapped:0.1.0
Образ при старте сам выполняет alembic upgrade head и поднимает uvicorn на порту 8000. Healthcheck: GET /health.
Kubernetes (Helm)
helm upgrade --install wrapped ./helm/wrapped \
-f my-values.yaml
В values.yaml задайте образ с Hub, внешние DB/S3 и секреты:
image:
repository: inecs/wrapped
tag: "0.1.0"
pullPolicy: IfNotPresent
app:
env: production
baseUrl: "https://wrapped.example.com"
secretKey: "..."
adminUsername: admin
adminPassword: "..."
docsEnabled: false
trustedProxies: "*" # если до пода достучаться может только Ingress
external:
databaseUrl: "postgresql+asyncpg://..."
s3:
endpointUrl: "https://..."
accessKey: "..."
secretKey: "..."
bucket: wrapped
createBucket: false
Упаковка чарта: make helm-package IMAGE_TAG=0.1.0.
Make-цели (разработка)
| Цель | Описание |
|---|---|
make env |
.env из .env.example |
make up |
Поднять dev-стек |
make down / make clean |
Остановить / с volumes |
make logs / make ps |
Логи / статус |
make migrate |
alembic upgrade head |
make release |
Сборка образа (+ push) и Helm package |
make helm-lint / make helm-package |
Helm |
Требования
- Python 3.12+ (для запуска без Docker)
- PostgreSQL 16+
- S3-совместимое хранилище (MinIO и т.п.)
- Docker Compose v2 — для деплоя как выше
Лицензия
Proprietary / на ваше усмотрение.







