Note
The project was previously called 4tip, so in preparation for version 1.0 a migration was made to the new name, but in some places the old name still remains. In the invitation codes generated for 4TIP registration, “4TIP” remains at the beginning of the code to make the code type easier to identify and understand.
A donation platform that emphasizes privacy, anonymity, and security, as well as support for social features and federation via the ActivityPub protocol. It serves as a self-hosted alternative to projects like Patreon and Boosty.
Instances:
- cryptovich.info.gf (official, is also available on old subdomain for viewing, but the login and some features won't work)
Note
Since the project was originally designed for Russian-speaking users, you may come across some Russian text here and there. The changelog is written exclusively in Russian (but hey, everyone knows how to use a translator, and if something isn't clear, a neural network can help out — that's the world we live in).
Modular monolith, not microservices:
apps/web— Next.js (App Router) frontendapps/api— Fastify backend, organized as domain modules (auth,tokens,creators,products,bundles,store,payments,donations,subscriptions,library,connections,stickers,federation,analytics,reports,admin,comments,notifications,streams,podcasts,feed-sources)apps/worker— every background job: payment/subscription expiry (30s), sticker import processing (5s), federation delivery with retry + backoff (10s), dual-feed ingestion (60s)packages/db— Prisma schema + generated client, shared by api/workerpackages/shared— cross-cutting domain types and theMoneyvalue typepackages/integrations— external provider interfaces + implementations (Telegram/Sharkey stickers, ActivityPub federation with real HTTP Signature request signing), shared by api and worker
- Install full Docker and Docker Compose
- Clone, edit
.envfile (this is just a simple example, but I still recommend using the production file.env.production.exampleright away) and launch!
git clone https://github.com/palmdm/Cryptovich && cd Cryptovich
cp .env.example .envWarning
Please note that you must fill in the .env file with the database passwords, the administrator account, the domain, and the full path to the cloned project directory! Be sure to check the entire .env file before running the project!
And now:
chmod +x ./scripts/upgrade.sh && ./scripts/upgrade.shor
docker compose up -d --build && docker builder prune -fVisit http://localhost:3000, register an account, "Become a creator" from
the dashboard, then publish a free or paid post. Paid posts route through
the mock crypto checkout — see below.
There's deliberately no self-serve "become admin" route — granting admin access via an HTTP endpoint is itself a privilege-escalation surface worth not having. Promote the first admin directly in the database:
UPDATE "User" SET role = 'ADMIN' WHERE username = 'your-username';They'll see the Admin link in the nav on next load, and every /admin/*
route re-checks role === 'ADMIN' server-side regardless of what the
frontend shows.
There is no live blockchain integration yet. MockPaymentProvider
(apps/api/src/modules/payments/mock-provider.ts) implements the same
PaymentProvider interface a real BTC/EVM adapter would, and simulates the
full payment state machine: AWAITING_PAYMENT → DETECTED → CONFIRMING → CONFIRMED, plus FAILED / EXPIRED / UNDERPAID / OVERPAID. The same
state machine backs product purchases, bundle purchases, donations and
subscription checkouts — one PaymentService, one idempotency mechanism.
In development, the product page exposes "Simulate: payment detected /
next confirmation / payment failed" buttons that call
POST /payments/:id/dev-advance — a dev/test-only endpoint (returns 404 in
production) that drives the same reconciliation path a real chain poller
would use. This lets the entire checkout → purchase → entitlement flow be
exercised without any real crypto.
Swapping in a real chain adapter later means implementing PaymentProvider
against a real RPC/indexer and pointing PAYMENT_PROVIDER at it —
PaymentService, EntitlementService, routes and the frontend do not
change.
packages/integrations holds real (not just mocked) implementations
alongside their mocks:
- Telegram (
TelegramStickerProvider) — uses the Bot API'sgetStickerSet/getFile, which need onlyTELEGRAM_BOT_TOKEN, no per-creator OAuth. - Sharkey (
SharkeyStickerProvider) — hits a creator-provided instance's public/api/emojisendpoint. - ActivityPub (
ActivityPubProvider) — builds and delivers real, HTTP Signature-signedCreate/Update/Deleteactivities. It does not yet serve actor/WebFinger documents for this platform's own creators (seedocs/FEDERATION.md), so live interoperability with an unmodified Mastodon/Sharkey instance needs that follow-up piece — the signing and delivery mechanics themselves are real and tested (including verifying a generated signature cryptographically, and delivering to a local fake inbox in the worker's integration tests).
pnpm test # unit tests — Money, TOTP (against the RFC 4226 vector),
# mock payment/sticker/federation providers, HTTP Signature
# signing — no DB required for these
pnpm typecheck
pnpm lint
pnpm buildapps/api/test/*.test.ts and apps/worker/test/*.test.ts are integration
tests that boot the real app (or call the real worker job function)
against a real Postgres database (via DATABASE_URL), including spinning
up a local fake HTTP server to exercise real sticker-import and
federation-delivery network code without touching the real internet. They
require Postgres to be reachable (docker compose up -d postgres).
- Interactive, hand-written OpenAPI 3.0 docs are served at
GET /docs(Swagger UI) once the API is running — browsable and directly callable, covering the creator-automation and admin surface in particular. The fuller prose reference isdocs/API.md. - Personal access tokens (
POST /tokens, prefixedpat_) let a creator's own script authenticate withAuthorization: Bearer <token>instead of a browser session — spec: "a mini-API for the convenience of automation for the author." Managed from/dashboard/tokensor directly against the API.
docker-compose.yml builds apps/api, apps/worker and apps/web as
separate images alongside Postgres and Redis, with a named volume for
uploaded files (UPLOAD_DIR, mounted at /data/uploads in the api
container so it survives a recreate). The Dockerfiles are functional but
not yet layer-cache-optimized (no lockfile is committed yet, so
pnpm install always resolves fresh) — tightening that, plus production
observability beyond the /health DB+Redis check, remains open.
See docs/DEPLOYMENT.md for the full deploy walkthrough, environment
variable guidance, and the backup/restore scripts
(scripts/backup.sh/scripts/restore.sh) — a single timestamped archive
(database + uploads) that's also how you relocate or clone the whole
platform onto a different server.
See .env.example. Every external integration (payments, federation,
sticker import providers, object storage) is read through an env var and an
abstraction interface — nothing in business logic imports a vendor SDK
directly.