Node flags (cmd/node/main.go):
--max-cpu / --max-ram-mb — Go runtime caps (GOMAXPROCS / GOMEMLIMIT)
--feed-disk-limit-mb — hard 507 refusal for new post bodies over quota
--chain-disk-limit-mb — advisory watcher (can't reject blocks without
breaking consensus; logs WARN every minute)
Client — Saved Messages (self-chat):
- Auto-created on sign-in, pinned top of chat list, blue bookmark avatar
- Send short-circuits the relay (no encrypt, no fee, no mailbox hop)
- Empty state rendered outside inverted FlatList — fixes the mirrored
"say hi…" on Android RTL-aware layout builds
- PostCard shows "You" for own posts instead of the self-contact alias
Client — user walls:
- New route /(app)/feed/author/[pub] with infinite-scroll via
`created_at` cursor and pull-to-refresh
- Profile screen gains "View posts" button (universal) next to
"Open chat" (contact-only)
Feed pipeline:
- Bump client JPEG quality 0.5 → 0.75 to match server scrubber (Q=75),
so a 60 KiB compose doesn't balloon past 256 KiB after server re-encode
- ErrPostTooLarge now wraps with the actual size vs cap, errors.Is
preserved in the HTTP layer
- FeedMailbox quota + DiskUsage surface — supports new CLI flag
README:
- Step-by-step "first node / joiner" section on the landing page,
full flag tables incl. the new resource-cap group, minimal
checklists for open/private/low-end deployments
DChain
Блокчейн-стек для децентрализованного мессенджера + социальной ленты:
- PBFT консенсус с multi-sig validator governance и equivocation slashing
- Native Go контракты рядом с WASM (wazero) — нулевая задержка для
системных сервисов типа
username_registry - WebSocket push API — клиент не опрашивает, все события прилетают на соединение
- E2E-шифрованный relay mailbox на libp2p gossipsub с TTL live-detection (1:1 чаты через NaCl box; посты в ленте — plaintext-публичные)
- Социальная лента v2.0.0 (заменила каналы): публичные посты с оплатой за размер (автор платит, хостящая релей-нода получает); on-chain граф подписок + лайки; off-chain просмотры + хэштеги; мандаторный server-side scrubber метаданных (EXIF/GPS-стрип + FFmpeg sidecar для видео); share-to-chat c embedded post-карточкой
- Система обновлений: build-time версия →
/api/well-known-version, peer-version gossip,/api/update-checkпротив Gitea releases,update.shс semver guard - Prometheus
/metrics, Caddy auto-HTTPS, observer mode, load-test
Содержание
- Быстрый старт
- Поднятие ноды — пошагово
- Продакшен деплой
- Архитектура
- REST / WebSocket API
- CLI
- Мониторинг
- Тесты
- Документация
Быстрый старт
Одна нода в Docker, HTTP API на localhost:8080, Explorer UI и Swagger
открыты:
# 1. Собираем prod-образ
docker build -t dchain-node-slim -f deploy/prod/Dockerfile.slim .
# 2. Ключ ноды (один раз)
mkdir -p keys
docker run --rm --entrypoint /usr/local/bin/client \
-v "$PWD/keys:/out" dchain-node-slim \
keygen --out /out/node.json
# 3. Запуск (genesis-валидатор)
docker run -d --name dchain --restart unless-stopped \
-p 4001:4001 -p 8080:8080 \
-v dchain_data:/data \
-v "$PWD/keys:/keys:ro" \
-e DCHAIN_GENESIS=true \
-e DCHAIN_ANNOUNCE=/ip4/127.0.0.1/tcp/4001 \
dchain-node-slim \
--db=/data/chain --mailbox-db=/data/mailbox --key=/keys/node.json \
--relay-key=/data/relay.json --listen=/ip4/0.0.0.0/tcp/4001 --stats-addr=:8080
# 4. Проверка
open http://localhost:8080/ # Explorer
open http://localhost:8080/swagger # Swagger UI
curl -s http://localhost:8080/api/well-known-version | jq .
3-node dev-кластер (для тестов PBFT кворума, slashing, federation): docker compose up --build -d — см. docs/quickstart.md.
Поднятие ноды — пошагово
Ниже — полный минимум для двух сценариев, которые покрывают 99% случаев:
первая нода сети (genesis) и присоединение к существующей сети.
Все флаги читаются также из соответствующего DCHAIN_* env-var (CLI > env > default).
Шаг 1. Ключи
# Ключ identity ноды (Ed25519 — подпись блоков + tx)
./client keygen --out keys/node.json
# relay-ключ (X25519 — E2E-mailbox) создаётся нодой сам при первом старте,
# но можно задать путь заранее через --relay-key.
Шаг 2a. Первая нода (genesis)
Поднимает новую сеть с одним валидатором. --genesis=true только для самой первой ноды и только один раз — если блок 0 уже есть в --db, флаг игнорируется.
./node \
--genesis=true \
--key=keys/node.json \
--db=./chaindata \
--mailbox-db=./mailboxdata \
--feed-db=./feeddata \
--listen=/ip4/0.0.0.0/tcp/4001 \
--announce=/ip4/<ВАШ-ПУБЛИЧНЫЙ-IP>/tcp/4001 \
--stats-addr=:8080 \
--register-relay=true \
--relay-fee=1000
--announce обязателен для любой ноды смотрящей в интернет (VPS / внешний IP / Docker с проброшенным портом). Без него libp2p пытается UPnP/NAT-PMP и чаще всего промахивается.
Шаг 2b. Вторая и последующие ноды
Нужен один из двух способов узнать первую ноду. Второй удобнее.
Через HTTP URL живой ноды (рекомендуется — нода сама заберёт multiaddr через /api/network-info, проверит genesis_hash и синхронизирует цепь):
./node \
--join=https://first-node.example.com \
--key=keys/node.json \
--db=./chaindata \
--mailbox-db=./mailboxdata \
--feed-db=./feeddata \
--listen=/ip4/0.0.0.0/tcp/4001 \
--announce=/ip4/<ВАШ-ПУБЛИЧНЫЙ-IP>/tcp/4001 \
--stats-addr=:8080 \
--register-relay=true \
--relay-fee=1000
Через libp2p multiaddr (если есть прямой мульти-адрес):
./node \
--peers=/ip4/1.2.3.4/tcp/4001/p2p/12D3KooW... \
# остальные флаги как выше
Автоприсоединение к validator set происходит не само: после того как нода синхронизируется, действующий validator должен вызвать client add-validator --target <your-pub> --cosigs ... (multi-sig admit). До этого новая нода живёт как observer — читает и гоняет tx, но не голосует. Запустить ноду явно как observer (никогда не проситься в validator set): --observer=true.
Все флаги node
CLI / env / default. Группы:
Storage
| Флаг | Env | Default | Назначение |
|---|---|---|---|
--db |
DCHAIN_DB |
./chaindata |
BadgerDB блокчейна |
--mailbox-db |
DCHAIN_MAILBOX_DB |
./mailboxdata |
E2E-конверты 1:1 чатов |
--feed-db |
DCHAIN_FEED_DB |
./feeddata |
Тела постов ленты (off-chain) |
--feed-ttl-days |
DCHAIN_FEED_TTL_DAYS |
30 |
Через сколько дней тела постов auto-evict'ятся (метаданные on-chain остаются вечно) |
Identity
| Флаг | Env | Default | Назначение |
|---|---|---|---|
--key |
DCHAIN_KEY |
./node.json |
Ed25519 ключ ноды |
--relay-key |
DCHAIN_RELAY_KEY |
./relay.json |
X25519 ключ для relay-mailbox (создастся сам) |
--wallet |
DCHAIN_WALLET |
— | Отдельный payout-кошелёк (опционально) |
--wallet-pass |
DCHAIN_WALLET_PASS |
— | Парольная фраза для wallet-файла |
Network
| Флаг | Env | Default | Назначение |
|---|---|---|---|
--listen |
DCHAIN_LISTEN |
/ip4/0.0.0.0/tcp/4001 |
libp2p listen multiaddr |
--announce |
DCHAIN_ANNOUNCE |
— | Multiaddr который нода рассказывает пирам (обязателен на VPS/внешнем IP) |
--peers |
DCHAIN_PEERS |
— | Bootstrap multiaddrs, comma-separated |
--join |
DCHAIN_JOIN |
— | HTTP URL живой ноды для авто-дискавери — получает peers и genesis_hash |
--allow-genesis-mismatch |
— | false |
Отключить защиту, падающую при расхождении локального и seed'ового genesis (только для явной миграции) |
Consensus & role
| Флаг | Env | Default | Назначение |
|---|---|---|---|
--genesis |
DCHAIN_GENESIS |
false |
Создать блок 0 (только для самой первой ноды сети) |
--validators |
DCHAIN_VALIDATORS |
— | Исходный validator set (CSV pub-keys) — применяется только при genesis |
--observer |
DCHAIN_OBSERVER |
false |
Observer-режим: синхронизируется и отдаёт API, но не голосует и не предлагает блоки |
--heartbeat |
DCHAIN_HEARTBEAT |
true |
Периодический HEARTBEAT-tx (нужен для liveness-детекции валидаторов) |
Relay / mailbox
| Флаг | Env | Default | Назначение |
|---|---|---|---|
--register-relay |
DCHAIN_REGISTER_RELAY |
false |
Отправить REGISTER_RELAY tx на старте (объявить ноду публичным relay'ем) |
--relay-fee |
DCHAIN_RELAY_FEE |
1000 |
Плата за доставку одного сообщения в µT (1000 = 0.001 T). 0 = бесплатный relay |
Media scrubber (feed)
| Флаг | Env | Default | Назначение |
|---|---|---|---|
--media-sidecar-url |
DCHAIN_MEDIA_SIDECAR_URL |
— | URL FFmpeg-сайдкара для видео-скраба. Пустой = только картинки |
--allow-unscrubbed-video |
DCHAIN_ALLOW_UNSCRUBBED_VIDEO |
false |
Принимать видео без серверного скраба (опасно — EXIF/GPS/автор-теги останутся) |
HTTP API
| Флаг | Env | Default | Назначение |
|---|---|---|---|
--stats-addr |
DCHAIN_STATS_ADDR |
:8080 |
Адрес HTTP/WS сервера |
--api-token |
DCHAIN_API_TOKEN |
— | Bearer-токен для submit tx. Пустой = публичная нода |
--api-private |
DCHAIN_API_PRIVATE |
false |
Требовать токен также на чтение |
--disable-ui |
DCHAIN_DISABLE_UI |
false |
Отключить HTML-explorer (JSON API остаётся) |
--disable-swagger |
DCHAIN_DISABLE_SWAGGER |
false |
Отключить /swagger* |
Resource caps (новое в v2.1.0)
| Флаг | Env | Default | Назначение |
|---|---|---|---|
--max-cpu |
DCHAIN_MAX_CPU |
0 |
Сколько CPU-ядер Go-runtime'у (GOMAXPROCS). 0 = все |
--max-ram-mb |
DCHAIN_MAX_RAM_MB |
0 |
Soft-лимит Go-хипа в MiB (GOMEMLIMIT). 0 = без лимита. Не OOM-kill'ит — усиливает GC при приближении |
--feed-disk-limit-mb |
DCHAIN_FEED_DISK_LIMIT_MB |
0 |
Жёсткая квота на feed-БД. При превышении /feed/publish отвечает 507. Существующие посты продолжают отдаваться |
--chain-disk-limit-mb |
DCHAIN_CHAIN_DISK_LIMIT_MB |
0 |
Advisory-квота на блокчейн-БД. Превышение → WARN в лог раз в минуту (жёстко не отказываем — сломали бы консенсус) |
Для реального sandboxing (hard-kill при OOM, hard CPU throttling) используйте docker run --cpus --memory или systemd CPUQuota / MemoryMax поверх этих флагов.
Update / versioning
| Флаг | Env | Default | Назначение |
|---|---|---|---|
--update-source-url |
DCHAIN_UPDATE_SOURCE_URL |
— | Gitea /api/v1/repos/{owner}/{repo}/releases/latest для /api/update-check |
--update-source-token |
DCHAIN_UPDATE_SOURCE_TOKEN |
— | PAT для приватного репо |
--log-format |
DCHAIN_LOG_FORMAT |
text |
text (human) или json (Loki/ELK) |
--governance-contract |
DCHAIN_GOVERNANCE_CONTRACT |
— | ID governance-контракта для динамических параметров |
--version |
— | — | Печатает версию и выходит |
Минимальные чек-листы
Первая нода (открытая): --genesis=true + --key + --announce на внешний IP + --stats-addr + опционально --register-relay=true --relay-fee=... чтобы сразу монетизировать relay-трафик.
Joiner: --join=<url-любой-живой-ноды> + --key + --announce + --stats-addr. После синка попросите действующего валидатора поднять add-validator (иначе остаётесь observer'ом до принятия — это нормально и безопасно).
Приватная/домашняя нода без публичного эксплорера: добавьте --api-token=<random>, --api-private=true, --disable-ui=true, --disable-swagger=true. Clients передают Authorization: Bearer <token>.
Слабое железо: --max-cpu=2 --max-ram-mb=1024 --feed-disk-limit-mb=2048 --chain-disk-limit-mb=10240.
Docker-обёртка с теми же флагами — в deploy/single/README.md.
Продакшен деплой
Два варианта, по масштабу.
🔸 Single-node (deploy/single/)
Рекомендуется для личного/первого узла. Один узел + Caddy TLS +
опциональный Prometheus. Полный runbook с 6 сценариями (публичная с UI,
headless, полностью приватная, genesis, joiner, auto-update) — в
deploy/single/README.md.
Модели доступа
| Режим | DCHAIN_API_TOKEN |
DCHAIN_API_PRIVATE |
Поведение |
|---|---|---|---|
| Public (default) | не задан | — | Все могут читать и писать |
| Token writes | задан | false |
Читать — любой; submit tx — только с токеном |
| Fully private | задан | true |
Всё требует Authorization: Bearer <token> |
UI / Swagger — включать или нет?
| Нужно | DCHAIN_DISABLE_UI |
DCHAIN_DISABLE_SWAGGER |
Открыто |
|---|---|---|---|
| Публичная с эксплорером + docs | не задано | не задано | / Explorer, /swagger, /api/*, /metrics |
| Headless API-нода с OpenAPI | true |
не задано | /swagger, /api/*, /metrics |
| Личная hardened | true |
true |
/api/* + /metrics |
Флаги читаются и из CLI (--disable-ui, --disable-swagger), и из env.
/api/* JSON-поверхность регистрируется всегда — отключить её можно
только на уровне Caddy / firewall.
Auto-update
# node.env — когда проект в Gitea
DCHAIN_UPDATE_SOURCE_URL=https://gitea.example.com/api/v1/repos/OWNER/REPO/releases/latest
UPDATE_ALLOW_MAJOR=false
# Hourly systemd timer с 15-мин jitter
sudo cp deploy/single/systemd/dchain-update.{service,timer} /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now dchain-update.timer
Подробно: docs/update-system.md + deploy/UPDATE_STRATEGY.md.
🔹 Multi-validator (deploy/prod/)
3 validator'а в PBFT-кворуме, Caddy с ip_hash для WS-стикинесса и
least-conn для REST. Для федераций / консорциумов — см.
deploy/prod/README.md и
docs/node/multi-server.md.
Архитектура
┌────────────┐ libp2p ┌────────────┐ libp2p ┌────────────┐
│ node-A │◄─────pubsub──►│ node-B │◄─────pubsub──►│ node-C │
│ validator │ │ validator │ │ validator │
│ + relay │ │ + relay │ │ + relay │
└─────┬──────┘ └─────┬──────┘ └─────┬──────┘
│ │ │
│ HTTPS / wss (via Caddy) │ │
▼ ▼ ▼
mobile / web / CLI clients (ip_hash для WS, least-conn для REST)
Четыре слоя: network → chain → transport → app. Детали — в
docs/architecture.md.
Основные пакеты:
| Путь | Роль |
|---|---|
blockchain/ |
Блочная машина, applyTx, native-контракты, schema-migrations |
consensus/ |
PBFT (Pre-prepare → Prepare → Commit), мемпул, equivocation |
p2p/ |
libp2p host, gossipsub, sync протокол, peer-version gossip |
node/ |
HTTP + WS API, SSE, metrics, access control |
node/version/ |
Build-time version metadata (ldflags-инжектимый) |
vm/ |
wazero runtime для WASM-контрактов + gas model |
relay/ |
E2E mailbox (1:1 envelopes) + public feed-mailbox (post bodies, view counter, hashtag index) |
media/ |
Server-side metadata scrubber (EXIF strip + FFmpeg sidecar client) |
identity/ |
Ed25519 + X25519 keypair, tx signing |
economy/ |
Fee model, rewards |
wallet/ |
Optional payout wallet (отдельный ключ) |
REST / WebSocket API
Обзор (полный reference — docs/api/README.md):
Chain + stats
| Endpoint | Описание |
|---|---|
GET /api/netstats |
height, total_txs, supply, validator_count |
GET /api/network-info |
chain_id, genesis, peers, validators, contracts |
GET /api/blocks?limit=N / /api/block/{index} |
Блоки |
GET /api/tx/{id} / /api/txs/recent?limit=N |
Транзакции |
GET /api/address/{pub_or_addr} |
Баланс + история |
GET /api/validators |
Validator set |
GET /api/peers |
Connected peers + их версии |
POST /api/tx |
Submit signed tx (rate-limited, size-capped) |
Discovery + update
| Endpoint | Описание |
|---|---|
GET /api/well-known-version |
{node_version, build{}, protocol_version, features[], chain_id} |
GET /api/well-known-contracts |
{name → contract_id} map |
GET /api/update-check |
Diff с Gitea release (см. docs/update-system.md) |
Real-time
GET /api/ws— WebSocket (recommended). Ops:auth,subscribe,unsubscribe,submit_tx,typing,ping. Push:block,tx,inbox,typing,submit_ack.GET /api/events— SSE legacy one-way.
Scoped WS-топики (addr:, inbox:, typing:) требуют auth через
Ed25519-nonce; публичные (blocks, tx, contract_log) — без.
Relay (E2E messaging)
| Endpoint | Описание |
|---|---|
POST /relay/broadcast |
Опубликовать pre-sealed envelope (E2E-путь, рекомендован) |
GET /relay/inbox?pub=<x25519> |
Прочитать входящие конверты |
DELETE /relay/inbox/{id} |
Удалить envelope (требует Ed25519-подписи владельца) |
Детали — docs/api/relay.md. /relay/send оставлен
для backward-compat, но ломает E2E (nod-релей запечатывает своим ключом)
и помечен как non-recommended.
Social feed (v2.0.0)
| Endpoint | Описание |
|---|---|
POST /feed/publish |
Загрузить тело поста + EXIF-скраб + вернуть fee |
GET /feed/post/{id} |
Тело поста |
GET /feed/post/{id}/attachment |
Сырые байты картинки/видео (cache'able) |
GET /feed/post/{id}/stats?me=<pub> |
{views, likes, liked_by_me?} |
POST /feed/post/{id}/view |
Бамп off-chain счётчика просмотров |
GET /feed/author/{pub}?before=<ts>&limit=N |
Посты автора (пагинация before) |
GET /feed/timeline?follower=<pub>&before=<ts>&limit=N |
Merged лента подписок |
GET /feed/trending?window=24&limit=N |
Топ по likes × 3 + views за окно |
GET /feed/foryou?pub=<pub>&limit=N |
Рекомендации (неподписанные авторы) |
GET /feed/hashtag/{tag}?limit=N |
Посты по хэштегу |
Детали + спецификация — docs/api/feed.md.
Docs / UI
GET /swagger— Swagger UI (рендерится через swagger-ui-dist).GET /swagger/openapi.json— сырая OpenAPI 3.0 спека.GET /— block-explorer HTML (выключаетсяDCHAIN_DISABLE_UI=true).
CLI
client keygen --out key.json
client --version # → dchain-client vX.Y.Z (commit=... date=...)
client balance --key key.json --node URL
client transfer --key key.json --to <pub> --amount <µT> --node URL
client call-contract --key key.json --contract native:username_registry \
--method register --arg alice --amount 10000 \
--node URL
client add-validator --key key.json --target <pub> --cosigs pub:sig,pub:sig
client admit-sign --key validator.json --target <candidate-pub>
client deploy-contract --key key.json --wasm ./my.wasm --abi ./my_abi.json --node URL
# полный список — `client` без аргументов, или в docs/cli/README.md
Все флаги node — в docs/node/README.md, либо
node --help.
Мониторинг
Prometheus endpoint /metrics на каждой ноде. Ключевые метрики:
dchain_blocks_total # committed blocks count
dchain_txs_total # tx count
dchain_tx_submit_accepted_total
dchain_tx_submit_rejected_total
dchain_ws_connections # current WS sockets
dchain_peer_count_live # live libp2p peer count
dchain_max_missed_blocks # worst validator liveness gap
dchain_block_commit_seconds # histogram of AddBlock time
Grafana + Prometheus поднимаются вместе с нодой через
docker compose --profile monitor up -d (см. deploy/single/docker-compose.yml).
Тесты
# Unit + integration
go test ./... # blockchain + consensus + identity + relay + vm
go vet ./... # static checks
# End-to-end load (3-node dev cluster должен быть поднят):
docker compose up --build -d
go run ./cmd/loadtest \
--node http://localhost:8081 \
--funder testdata/node1.json \
--clients 50 --duration 60s
Документация
Полный справочник в docs/ — см. docs/README.md как
оглавление. Ключевые документы:
| Документ | О чём |
|---|---|
docs/quickstart.md |
Три пути: локально, single-node prod, федерация |
docs/architecture.md |
4 слоя, consensus, storage, gas model |
docs/update-system.md |
Versioning + update-check + semver guard |
docs/api/README.md |
REST + WebSocket reference |
docs/cli/README.md |
CLI команды и флаги |
docs/node/README.md |
Запуск ноды (native + Docker) |
docs/contracts/README.md |
Системные + WASM контракты |
docs/development/README.md |
SDK для своих контрактов |
deploy/single/README.md |
Single-node operator runbook |
deploy/prod/README.md |
Multi-validator federation |
deploy/UPDATE_STRATEGY.md |
Forward-compat design (4 слоя) |