SelfLink is an open-source backend + mobile app experimenting with a simple idea:
Can we build software where contributors are rewarded transparently, enforced by code instead of promises?
This repository contains the backend.
Start here: START_HERE.md
The mobile app lives here: https://github.com/georgetoloraia/selflink-mobile
- Django / DRF backend running on a real domain
- React Native / Expo mobile app connected to it
- Auth and core flows working end-to-end
- Append-only contributor rewards ledger
- Deterministic monthly reward calculation (no manual edits)
This is an early-stage project, but it is already real and working.
Every merged contribution becomes an immutable event; events are aggregated monthly; rewards are calculated mechanically. Everything else is an implementation detail.
Most platforms rely on trust, discretion, or closed accounting when it comes to value distribution. SelfLink tries a different approach:
- no retroactive edits
- no hidden rules
- no subjective reward decisions If something is rewarded, it is:
- recorded
- auditable
- reproducible
Mobile App (Expo)
|
v
Django API (DRF)
|
v
RewardEvent Ledger (append-only)
|
v
Monthly Snapshot → Payouts
There are three simple ways to help:
- Backend (Django / DRF): See CONTRIBUTING.md
- Mobile (React Native / Expo): https://github.com/georgetoloraia/selflink-mobile
- Architecture / design feedback: Open an issue — no code required
If you’re new, start with a good first issue.
- 50% of future net platform revenue is reserved for contributors
- Contributions are tracked as immutable RewardEvents
- Rewards are calculated monthly using deterministic rules
- Corrections happen via new events, never by rewriting history
Full details: CONTRIBUTOR_REWARDS.md
- a DAO
- a token or crypto project
- a finished product
- a promise of guaranteed income
It is an experiment in trust, transparency, and simplicity.
- Actively developed
- Open to contributors
- Early feedback is especially valuable
If something feels unclear or over-engineered, that’s a bug — please point it out.
- Clone the repo and copy
infra/.env.exampletoinfra/.env(keep$$escapes for Compose) make infra-up(starts api + asgi + worker + postgres + redis + pgbouncer)make infra-migratemake infra-superusermake infra-status(quick health check for api/asgi ports)- Optional search stack:
docker compose -f infra/compose.yaml --profile search up -d - For more, see
README_for_env.mdordocker_guide.md
Note: Docker Compose interpolates $VAR in infra/.env; escape literal $ as $$ to avoid warnings.
- ASGI dev server:
uvicorn core.asgi:application --host 0.0.0.0 --port 8001 - Mentor SSE endpoint:
/api/v1/mentor/stream/(served from ASGI)
- Routing for
api.self-link.com:- REST + docs (
/api/v1/*,/api/docs/) -> API (http://localhost:8000) - WebSockets (
/ws*) -> ASGI (http://localhost:8001) - Media (
/media/*) -> static media server (http://localhost:8080)
- REST + docs (
cloudflaredruns on the host/systemd; useinfra/cloudflared/config.ymland replace:tunnel: <TUNNEL_UUID>credentials-file: /etc/cloudflared/<TUNNEL_UUID>.json
- The media service is published only to
127.0.0.1:8080for the host tunnel. SERVE_MEDIAis a dev fallback only; keep itfalsein production.
Cache guidance:
- Cloudflare can cache 404s; purge cache if media was missing before.
- While testing, add a Cache Rule to bypass cache for
/media/*. - Once stable, you can cache
/media/*with a long TTL. - Use
?v=timestampto bust cache during development.
STORAGE_BACKEND=local(default ininfra/compose.yaml).- Keep the
media-datavolume and themedianginx service; route/media/*tohttp://localhost:8080. SERVE_MEDIA=trueis a dev-only fallback when using Django for/media/.- Verification:
- Upload a new avatar/post image/video.
- Confirm file exists in the API container:
docker compose -f infra/compose.yaml exec api ls -lah /app/media/<path> - Origin check (media service):
curl -I http://localhost:8080/media/<path> - Public check:
curl -I https://api.self-link.com/media/<path>?v=1 - Diagnostics:
python manage.py storage_status
STORAGE_BACKEND=s3- Set
S3_ENDPOINT,S3_ACCESS_KEY_ID,S3_SECRET_ACCESS_KEY,S3_BUCKETininfra/.env. - Optional:
S3_QUERYSTRING_AUTH=truefor presigned URLs (default) orfalsefor public bucket URLs. - Start MinIO with
docker compose -f infra/compose.yaml --profile storage up -d minio(or point to AWS S3). - Verification:
- Upload a new avatar/post image/video.
- Confirm the object exists in the bucket (MinIO console or client).
- API should return an absolute URL (starts with
http). curl -I <absolute-media-url>- Diagnostics:
python manage.py storage_status
/api/v1/mentor/chat/acceptsX-LLM-Keyfor user-supplied provider keys.
Open source. See LICENSE.