Skip to content

Repository files navigation

Kindred

Kindred

Compatibility-first dating + social platform
Open source. Privacy-first. No funny business.

StatusVersionPythonLicense


This project is in active development. Kindred is being built to launch as a live website. This repository exists as a transparency measure -- the full source code is public so users can see exactly what runs behind the site. No hidden data collection, no dark patterns, no funny business. This is the code.


What is Kindred?

Kindred is a dating and social platform built around genuine compatibility instead of swiping on photos. Users answer a detailed questionnaire covering personality, values, communication style, finances, and more. An 8-dimension matching engine scores compatibility and generates narratives explaining why two people might click.

Features

Matching Engine

  • 8-dimension compatibility scoring (personality, values, communication, financial, attachment, tradeoffs, semantic, dealbreaker)
  • Adaptive IRT questionnaire with a 1000+ item bank and high-information sequencing
  • Active-learning /api/questionnaire/next endpoint balances information gain across compatibility dimensions
  • Optional country-code calibration uses private local cohorts and preserves raw scores for auditability
  • Compatibility radar chart (canvas spider chart for 8 dimensions)
  • sentence-transformers embeddings for semantic similarity (MPNet by default, MiniLM fallback)
  • Match narratives, icebreakers, and coaching tips
  • Customizable dimension weights per user
  • Private post-date outcome learning adapts match weights while preserving manual priorities
  • Photo reveal at compatibility threshold
  • Daily curated Top Picks suggestions
  • Super Like with instant notification
  • Match expiry (7-day countdown without messaging)
  • Location-based matching with distance filtering
  • Mutual friends indicator
  • Smart conversation starters (personalized from shared interests)
  • Compatibility recalculation (re-score after questionnaire updates)
  • AI conversation suggestions (template + Puter.js powered)
  • Date feedback and rating system

Social Platform

  • Video calling via Jitsi Meet (in-app call initiation with WebSocket signaling)
  • Real-time WebSocket messaging with typing indicators and read receipts
  • Voice messages (MediaRecorder API recording + playback) with optional server-side transcripts for accessibility
  • Message reactions (emoji) and GIF search (Tenor API)
  • Message editing (5-minute grace period) and soft-delete
  • Message status indicators (sent/delivered/read)
  • Clipboard image paste in chat
  • Message search across conversations
  • Group chat with real-time WebSocket messaging
  • Polls in groups (create, vote, results)
  • Stories/Moments (24-hour ephemeral posts with gradient backgrounds)
  • MySpace-style profile pages with blog, comments, friends, photo gallery
  • Profile prompts (Hinge-style "Two truths and a lie", "My ideal Sunday", etc.)
  • Profile themes (Cosmic, Forest, Sunset, Ocean, Aurora)
  • Status updates and activity feed
  • Groups and events with RSVP + calendar view + local meetup discovery with a Leaflet heatmap
  • Optional ticketed event RSVPs with Stripe PaymentIntents, idempotent payment records, and signed webhook confirmation
  • Profile boost (premium visibility boost with countdown timer)
  • Video intros and music preferences with cross-user compatibility scoring
  • "This or That" compatibility games between matched pairs
  • Who Viewed Me and Who Liked You feeds
  • Guided onboarding tour for new users
  • Notification sounds (AudioContext two-tone beep)
  • Image cropping before upload (Canvas API)
  • Icebreaker games (Word Association, Would You Rather, 20 Questions)
  • Date scheduling with ICS calendar export and optional Jitsi video rooms
  • Shared match calendars with revocable, tokenized ICS subscription URLs
  • Blind date mode (48h no-photo/name, then reveal)
  • Matchmaker mode: friends can suggest profiles with recipient approval before a like is sent
  • Second look (review passed profiles)
  • Threaded replies (quote-reply in conversations)
  • Shared playlists between matched pairs
  • Event photo albums (shared galleries)
  • Profile badges (achievement system)
  • Story reactions (quick emoji reactions)
  • Pinned messages in conversations
  • Dark/Light theme toggle (Catppuccin Mocha/Latte)
  • Keyboard shortcuts with help overlay
  • Profile completeness coaching
  • Private local profile coach for clarity, specificity, warmth, and low-stakes vulnerability
  • Animated page transitions
  • Typing previews (opt-in)
  • WebSocket auto-reconnection with exponential backoff
  • Unread badge counts (tab badges, page title, per-conversation)
  • Emoji picker (40-emoji floating grid)
  • Availability status (active/away/busy/offline with profile badges)
  • Announcement banners (platform-wide dismissible notices)
  • Link preview scanning (suspicious URL warnings)
  • Swipe gestures on discover cards (touch left/right for pass/like)
  • Infinite scroll with lazy loading for activity feed
  • Image lightbox (full-screen photo viewer with pinch-zoom)
  • Pull-to-refresh on mobile
  • Onboarding progress bar during questionnaire
  • Skeleton shimmer loading placeholders
  • Dealbreaker quiz comparison between matches
  • Compatibility over time tracking with line chart
  • Profile endorsements (trait badges from friends/matches)
  • Shared interests visual tag comparison
  • Group post emoji reactions
  • Event chat (real-time messaging within events)
  • Read receipts toggle (privacy setting)
  • Smart notification digest (aggregated summary)
  • Slow reveal profiles (progressive info unlock stages)

Trust & Safety

  • Two-factor authentication (TOTP) with recovery codes
  • Incognito mode (browse without appearing in Who Viewed Me)
  • Session management (view/revoke active sessions)
  • Account deletion (GDPR-compliant full data removal)
  • Data export (GDPR-compliant download your data)
  • CCPA do-not-sell preference (enabled by default; Kindred does not sell personal information)
  • Right-to-explanation responses for match visibility, compatibility scoring, and suspension decisions
  • Selfie verification with admin review and local ML liveness (blink + head turn)
  • Sliding-window direct-message harassment signals with escalating warnings and recipient-side auto-mutes
  • Upload-time pHash + dHash matching against an operator-managed known-abuse hash corpus
  • Automated content filtering (profanity censoring, spam blocking)
  • Photo moderation queue with admin review
  • Profile blocking with undo grace period
  • Safety check-in timer with emergency contact alerts
  • Dealbreaker warnings on profile views
  • Message cooldown (rate limit for new matches)
  • Link preview scanning (suspicious URL warnings)
  • Rate-limited auth endpoints
  • Shared Redis rate limits and refresh-token sessions when KINDRED_REDIS_URL is configured
  • Optional ActivityPub-style federation with signed public actors and cross-instance match offers; private vault data stays local
  • File upload magic byte validation on all upload endpoints
  • S3-compatible object storage for photos, videos, voice messages, selfies, stories, and event media with local development fallback
  • JWT authentication on all user and admin endpoints
  • WebSocket JWT authentication (prevents impersonation)
  • XSS prevention (HTML escaping on all user-rendered content)
  • Persistent JWT secret (file-backed, survives restarts)
  • CORS locked to localhost by default
  • UPSERT-based profile saves (prevents CASCADE data loss)
  • Full UUID IDs (collision-safe)
  • Transaction-wrapped multi-statement operations
  • Input length limits on regex processing (DoS prevention)
  • HTML-escaped email templates
  • Structured report system (7 reason categories with auto-escalation)
  • Temporary/permanent suspensions with appeal workflow
  • Photo perceptual hashing (duplicate/stolen photo detection)
  • Conversation quality signals (response rate, reply time badges)

Admin Dashboard

  • Separate admin portal on its own port (all endpoints require admin auth)
  • Health check endpoint with server status monitoring
  • Database backup scheduler with rotation and restore (SQLite backup API)
  • Analytics dashboard with engagement metrics and charts
  • Content filter log viewer
  • Stories moderation
  • Session management (view/revoke all user sessions)
  • User management, group/event moderation
  • Verification and photo moderation review queues
  • Photo-safety event review API and hash-corpus import command
  • Safety report triage with escalation queue and status filtering
  • Suspension management (suspend/unsuspend with audit trail)
  • Appeal review queue (uphold/overturn suspension appeals)
  • Platform statistics
  • Audit log with action filtering
  • Webhook management (CRUD + HMAC-signed delivery)
  • Email template preview
  • Rate limit dashboard
  • Database vacuum controls
  • User search with detailed activity view
  • Announcement management (create/delete platform-wide notices)
  • Flagged content queue (flag/review/resolve with type filtering)
  • Bulk profile actions (deactivate, delete, verify multiple users)
  • CSV export (users, safety reports, analytics)
  • Engagement over time chart (signups, messages, matches line graph)
  • Shadow ban management (invisible content suppression)
  • Canned responses (reusable admin reply templates with usage tracking)
  • Feature flags (toggle features without deploy)
  • Request stats dashboard (total requests, error rate, avg response time, top endpoints)
  • Admin-to-user messaging (direct + batch send to all users)
  • Retention cohort chart (week-over-week retention rates)
  • User funnel visualization (signup → profile → questionnaire → match → message → date)
  • Request log cleanup controls

Platform

  • Progressive Web App (installable, offline caching)
  • Web push notifications
  • Premium subscription tier scaffolding
  • i18n framework (internationalization-ready with JSON locale files)
  • Audit log (admin action tracking)
  • Webhook system (configurable outbound webhooks)
  • HTML email templates (verification, reset, match notifications)
  • Database vacuum scheduler
  • API rate limit dashboard (admin)
  • Health check endpoint (/api/health)
  • Read-only public API (profiles, events, stats with API key auth)
  • OAuth social login scaffolding (Google, Apple)
  • API key management (admin create/revoke with HMAC hashing)

Tech Stack

LayerTechnology
BackendPython 3.12+, FastAPI, Uvicorn
DatabaseSQLite (WAL mode, thread-local pooling); PostgreSQL migration utility
Embeddingssentence-transformers (all-mpnet-base-v2, MiniLM fallback)
NarrativesPuter.js (client-side)
FrontendVanilla JS single-file SPA
AuthJWT (pyjwt + passlib/bcrypt), 2FA TOTP, WebSocket JWT
SecurityCORS (locked to localhost), slowapi rate limiting, magic byte validation, XSS escaping
JobsDramatiq workers over Redis (production), inline fallback (development)
ThemeCatppuccin Mocha (dark) / Latte (light)
DeployDocker, docker-compose, Caddy

Project Structure

kindred/
start.py # Turnkey launcher
app/
config.py # Centralized env-based config
main.py # User API server (120+ endpoints)
admin_app.py # Admin API server
ws_app.py # Dedicated WebSocket worker entrypoint
job_queue.py # Dramatiq broker and queue submission policy
tasks.py # Embedding and photo-moderation actors
object_storage.py # Local/S3-compatible media storage
object_storage_migration.py # Legacy uploads/ migration command
photo_safety.py # pHash/dHash upload screening and optional external hook
photo_safety_corpus.py # Hash-only corpus importer
selfie_liveness.py # MediaPipe blink + head-turn sequence analyzer
harassment.py # Explainable direct-message harassment signal scoring
database.py # SQLite CRUD (70+ tables)
engine.py # 8-dimension matching engine
questions.py # Questionnaire definitions
static/
index.html # User SPA
admin.html # Admin SPA
logo.svg # Logo
favicon.svg # Favicon
mobile/ # Optional React Native/Expo iOS + Android client

The 8 Dimensions

DimensionWhat it measures
PersonalityBig Five traits (OCEAN) cross-correlated with scenarios
ValuesCore life priorities and moral foundations
CommunicationConflict style, love language, emotional expression
FinancialSpending habits, financial goals, money attitudes
AttachmentAttachment style (secure, anxious, avoidant, fearful)
TradeoffsLife preference polarities (city/country, save/spend, etc.)
SemanticFree-text response similarity via sentence embeddings
DealbreakerHard compatibility filters

Running Locally (Development)

git clone https://github.com/SysAdminDoc/kindred.git
cd kindred
python start.py

The launcher auto-creates a virtual environment, installs dependencies, and starts both servers.

Native mobile client

The optional mobile/ Expo project targets iOS and Android and uses the same REST API for authentication, discovery, matches, and messages. Install Node.js dependencies from that directory and set EXPO_PUBLIC_API_URL to a URL that the device or emulator can reach (a LAN address, not 127.0.0.1, for a real device):

cd mobile
npm install
set EXPO_PUBLIC_API_URL=http://192.168.1.20:8000
npm run start

The mobile client does not duplicate account or profile storage; the existing Kindred server remains the source of truth.

Docker

cp .env.example .env
docker compose up --build

The root Docker Compose file and start.py are intentionally convenient two-server development launchers. For a horizontally scaled deployment, use the production stack instead:

cp deploy/.env.production .env
# Fill in the required values, then replace YOUR_DOMAIN in deploy/Caddyfile
docker compose -f deploy/docker-compose.prod.yml up --build -d

The production gateway sends user traffic to the user API workers on port 8000, admin traffic to the admin workers on port 8001, and /ws/* upgrades to dedicated WebSocket workers on port 8002. Redis supplies shared rate limits, refresh sessions, WebSocket pub/sub, connection presence, and the Dramatiq job broker; the production stack starts a persistent Redis service and requires it for every worker. The separate job worker handles profile embeddings and photo moderation queue records.

PostgreSQL migration

SQLite remains the local-development default. To migrate an existing database into an empty PostgreSQL database, install the normal requirements and run the explicit bridge command:

python -m app.postgres_migration --sqlite ./kindred.db --dry-run --json
export KINDRED_POSTGRES_DSN='postgresql://kindred:password@localhost/kindred'
python -m app.postgres_migration --sqlite ./kindred.db --postgres-dsn "$KINDRED_POSTGRES_DSN"

The command refuses a non-empty target, creates PostgreSQL-compatible tables, constraints, indexes, and identity sequences, streams rows in batches, and verifies per-table row counts before committing. It never drops or overwrites the SQLite source. The application continues using SQLite until a separately planned PostgreSQL runtime cutover is performed.

Configuration

Copy .env.example to .env to customize:

VariableDefaultDescription
KINDRED_JWT_SECRETauto-persistedJWT signing key (saved to .jwt_secret)
KINDRED_ADMIN_EMAILadmin@kindred.localDefault admin email
KINDRED_ADMIN_PASSWORDadminDefault admin password
KINDRED_HOST127.0.0.1Server bind address
KINDRED_USER_PORT8000User portal port
KINDRED_ADMIN_PORT8001Admin portal port
KINDRED_CORS_ORIGINSlocalhost:8000,8001Allowed CORS origins
KINDRED_RATE_LIMIT60/minuteGeneral rate limit
KINDRED_RATE_LIMIT_AUTH10/minuteAuth endpoint rate limit
KINDRED_REDIS_URLemptyRedis URL for shared rate limits, sessions, WebSocket pub/sub, and presence
KINDRED_REDIS_REQUIREDfalseFail startup instead of falling back when Redis is unavailable
KINDRED_REDIS_KEY_PREFIXkindredPrefix for Redis session keys
KINDRED_FEDERATION_ENABLEDfalseEnable ActivityPub-style actors, WebFinger, and signed cross-instance match offers
KINDRED_FEDERATION_BASE_URLemptyStable public HTTPS base URL for this instance; required when federation is enabled
KINDRED_FEDERATION_KEY_PATH.federation_ed25519.pemPersistent Ed25519 signing key path
KINDRED_FEDERATION_FETCH_TIMEOUT_SECONDS10Timeout for remote actor and inbox requests
KINDRED_FEDERATION_MAX_BODY_BYTES524288Maximum signed federation request/response size
KINDRED_FEDERATION_USER_AGENTKindred-Federation/1.0User-Agent sent to remote federation peers
KINDRED_JITSI_BASE_URLhttps://meet.jit.siBase URL used for optional scheduled video-date rooms
KINDRED_USER_WORKERS2User API worker count in the production Compose stack
KINDRED_ADMIN_WORKERS1Admin API worker count in the production Compose stack
KINDRED_WS_WORKERS2Dedicated WebSocket worker count in the production Compose stack
KINDRED_QUEUE_ENABLEDfalseEnable Dramatiq embedding and moderation jobs
KINDRED_QUEUE_REQUIREDfalseFail startup instead of falling back to inline jobs
KINDRED_QUEUE_PROCESSES2Dramatiq worker process count in the production Compose stack
KINDRED_QUEUE_THREADS2Dramatiq threads per worker process in the production Compose stack
KINDRED_TRANSCRIPTION_ENABLEDfalseEnable server-side voice transcription
KINDRED_TRANSCRIPTION_URLemptyOpenAI-compatible /audio/transcriptions endpoint; required when transcription is enabled
KINDRED_TRANSCRIPTION_API_KEYemptyOptional bearer token for the configured transcription endpoint
KINDRED_TRANSCRIPTION_MODELwhisper-1Model name sent to the transcription endpoint
KINDRED_TRANSCRIPTION_TIMEOUT_SECONDS120Per-audio transcription request timeout
KINDRED_INACTIVE_ACCOUNT_HARD_DELETE_MONTHS24Months after deactivation before scheduled hard deletion
KINDRED_PRIVACY_RETENTION_INTERVAL_HOURS24Scheduler interval for retention cleanup and inactive-account deletion
KINDRED_MAX_UPLOAD_MB30Max file upload size
KINDRED_OBJECT_STORAGE_ENDPOINTemptyS3-compatible endpoint; leave unset for local uploads/ storage
KINDRED_OBJECT_STORAGE_BUCKETemptyRemote media bucket; supplying it enables S3-compatible storage
KINDRED_OBJECT_STORAGE_ACCESS_KEYemptyS3-compatible access key or IAM-compatible credential
KINDRED_OBJECT_STORAGE_SECRET_KEYemptyS3-compatible secret key
KINDRED_OBJECT_STORAGE_REGIONus-east-1S3-compatible signing region
KINDRED_OBJECT_STORAGE_PREFIXmediaRemote object-key prefix; logical database keys remain unchanged
KINDRED_OBJECT_STORAGE_PUBLIC_URLemptyOptional public/CDN base URL; empty keeps media private behind /uploads/
KINDRED_OBJECT_STORAGE_REQUIREDfalseFail startup instead of serving new media from an unavailable or missing remote backend
KINDRED_OBJECT_STORAGE_ADDRESSING_STYLEauto (path with an endpoint)S3 addressing mode; path is recommended for MinIO and many S3-compatible services
KINDRED_PHOTO_HASH_ENABLEDtrueEnable local pHash + dHash matching against the known-abuse corpus
KINDRED_PHOTO_HASH_MAX_DISTANCE5Maximum pHash Hamming distance for a local corpus match
KINDRED_PHOTO_DHASH_MAX_DISTANCE8Maximum dHash Hamming distance for a local corpus match
KINDRED_PHOTO_SAFETY_REQUIREDfalseReject uploads if the enabled safety scan cannot complete
KINDRED_PHOTODNA_ENABLEDfalseEnable the operator-provided PhotoDNA hash webhook adapter
KINDRED_PHOTODNA_HOOK_URLemptyProvider-specific hash adapter URL, required when the hook is enabled
KINDRED_PHOTODNA_API_KEYemptyOptional subscription key passed to the configured adapter
KINDRED_PHOTODNA_TIMEOUT_SECONDS3External hash adapter timeout
KINDRED_HARASSMENT_ENABLEDtrueEnable direct-message harassment signal scoring
KINDRED_HARASSMENT_WINDOW_MINUTES10Sliding window used to aggregate sender/recipient signals
KINDRED_HARASSMENT_WARN_SCORE2Aggregate score at which a respectful-message warning is returned
KINDRED_HARASSMENT_MUTE_SCORE4Aggregate score at which the recipient-side auto-mute activates
KINDRED_HARASSMENT_MUTE_MINUTES60Auto-mute duration
KINDRED_REPORT_COOLING_OFF_DAYS30Days a reporter is excluded from seeing a reported profile; 0 is permanent
KINDRED_STRIPE_ENABLEDfalseEnable Stripe PaymentIntent ticketing for paid events
KINDRED_STRIPE_SECRET_KEYemptyStripe server-side secret key; required when ticketing is enabled
KINDRED_STRIPE_PUBLISHABLE_KEYemptyStripe browser key returned only for paid event checkout
KINDRED_STRIPE_WEBHOOK_SECRETemptyStripe webhook signing secret; required to confirm payment status
KINDRED_EVENT_PAYMENT_HOLD_MINUTES30Reservation hold for an unfinished paid RSVP
KINDRED_SELFIE_LIVENESS_ENABLEDtrueEnable local MediaPipe selfie liveness
KINDRED_SELFIE_LIVENESS_REQUIREDtrueRequire a passing blink + head-turn sequence before queueing verification
KINDRED_SELFIE_LIVENESS_MODEL_PATHmodels/face_landmarker.taskLocal Face Landmarker model asset
KINDRED_SELFIE_LIVENESS_MODEL_SHA256pinned asset hashFail startup if the model asset is not the pinned file
KINDRED_SELFIE_LIVENESS_MIN_FRAMES8Minimum ordered frames in a liveness attempt
KINDRED_SELFIE_LIVENESS_MAX_FRAMES24Maximum ordered frames accepted per attempt
KINDRED_SELFIE_LIVENESS_FRAME_INTERVAL_MS150Synthetic interval used when clients submit frames without timestamps
KINDRED_SELFIE_LIVENESS_MIN_DURATION_MS900Minimum sequence duration
KINDRED_SELFIE_LIVENESS_BLINK_CLOSED_EAR0.20Eye-aspect-ratio threshold for a closed-eye frame
KINDRED_SELFIE_LIVENESS_BLINK_OPEN_EAR0.24Eye-aspect-ratio threshold for an open-eye frame
KINDRED_SELFIE_LIVENESS_HEAD_TURN_DELTA0.12Relative nose/yaw movement required for a head turn
KINDRED_EMBEDDING_MODELall-mpnet-base-v2Preferred semantic embedding model
KINDRED_EMBEDDING_FALLBACK_MODELall-MiniLM-L6-v2Fallback when the preferred model cannot load

For a multi-worker production deployment, set KINDRED_REDIS_URL and KINDRED_REDIS_REQUIRED=true; the split user/admin/WebSocket topology also uses Redis pub/sub and short-lived presence leases to keep notifications and online status correct across processes. Leaving the requirement disabled intentionally keeps local development on SQLite sessions, in-memory rate limiting, process-local WebSocket delivery, and inline embedding/moderation work. Production should keep both queue flags enabled and run the supplied kindred-worker systemd service or Compose service.

Voice notes remain playable when transcription is disabled or temporarily unavailable. To add accessible transcripts, set KINDRED_TRANSCRIPTION_ENABLED=true and point KINDRED_TRANSCRIPTION_URL at an approved OpenAI-compatible /audio/transcriptions service (a local Whisper-compatible service works as well). Transcripts are generated in the dedicated kindred-transcription worker queue when Redis/Dramatiq is enabled; local development falls back to inline processing and exposes a pending or unavailable state in the conversation UI.

The privacy audit records a classification and retention strategy for every schema field and table. Administrators can inspect coverage at /api/admin/privacy/audit. Deactivated accounts are hard-deleted after KINDRED_INACTIVE_ACCOUNT_HARD_DELETE_MONTHS; the scheduled purge also removes unlinked OAuth, analytics, request-log, and media records for the account.

Authenticated users can inspect why a profile was shown or hidden by the matching policy at /api/explanations/match/{target_id} and can inspect their own current suspension and appeal state at /api/explanations/suspension. The equivalent /api/right-to-explanation/... paths are provided for clients that use the legal terminology directly.

GET /api/account/export returns a versioned Kindred JSON export containing the account's portable records. GET /api/account/export/schema-org returns a downloadable JSON-LD schema.org/Person profile for importing the identity and public profile fields into standards-aware tools.

Federation is disabled by default. To enable it, set KINDRED_FEDERATION_ENABLED=true and configure a stable public KINDRED_FEDERATION_BASE_URL; the first actor request creates the ignored, local Ed25519 key at KINDRED_FEDERATION_KEY_PATH. Public actors and WebFinger publish only profile fields intended for discovery. Match offers and decisions are signed before delivery to the remote inbox. Questionnaire answers, embeddings, messages, credentials, and other private vault data remain on the originating instance.

When object storage is configured, the API stores new media under the configured bucket and serves it through the existing /uploads/{key} contract; the default keeps the bucket private and supports photo, audio, and video byte ranges. Existing files in uploads/ remain readable as a migration fallback. Set KINDRED_OBJECT_STORAGE_REQUIRED=true in production so a missing bucket or unavailable endpoint fails startup instead of silently writing new media to local disk. The production environment template enables this fail-closed mode; replace its example endpoint and credentials before launch.

The local safety corpus stores only 64-bit pHash/dHash pairs and metadata. Import an operator-approved JSON corpus with python -m app.photo_safety_corpus corpus.json --source operator --dry-run, then rerun without --dry-run. Blocked upload metadata is visible to admins at /api/admin/photo-safety/events.

Direct messages are checked before delivery for explainable threat, sexual coercion, slur, and targeted-abuse signals. Signals are retained as category and score metadata only; repeated signals are aggregated per sender/recipient pair inside the configured sliding window. The first threshold returns a respectful-message warning, while the mute threshold creates a one-way recipient-side mute and suppresses later direct messages until it expires. The WebSocket message path applies the same gate, and administrators can review events and active mutes at /api/admin/harassment.

Reports immediately create a reporter-specific cooling-off exclusion, so the reported profile is removed from matches, suggestions, search, explore, nearby, availability, and recently-active/new-user discovery. The exclusion lasts KINDRED_REPORT_COOLING_OFF_DAYS days and is renewed by later reports; set it to 0 when the product policy requires a permanent exclusion. Reviewing or closing a report does not lift the exclusion automatically.

Event ticketing remains free and local by default. To enable paid events, set the Stripe keys and KINDRED_STRIPE_ENABLED=true; paid event creation then requires a ticket price of at least 50 cents. The RSVP endpoint creates one idempotent PaymentIntent per active attempt and returns only its client secret to the browser. Stripe webhook signatures are verified before an RSVP changes from pending to paid, and unfinished attempts reserve a seat only for the configured hold window. The host is automatically marked as attending without being charged.

Matched pairs can also create a shared calendar subscription from the date scheduling panel. The URL is a revocable bearer credential stored only as a hash; it includes both proposed and confirmed dates, reflects cancellations, and stops serving as soon as the pair is blocked, reported, or no longer mutually matched. The one-off .ics export remains authenticated to the date participants.

Selfie verification now captures twelve camera frames in the user portal and posts them as a short ordered sequence to /api/verify/selfie/{profile_id}. The server runs the bundled MediaPipe Face Landmarker locally, requires a blink and measurable head turn, stores only the first passing frame, and keeps aggregate liveness evidence with the admin review record. If camera access is unavailable, users can submit at least eight still frames through the fallback picker. Set KINDRED_SELFIE_LIVENESS_REQUIRED=false only for a deliberately degraded legacy still-image workflow.

To migrate an existing local upload directory, first configure the remote backend and run python -m app.object_storage_migration --dry-run, then rerun without --dry-run. The command is repeatable and leaves local files in place until the remote bucket has been verified.

For a bare-metal systemd deployment, install deploy/kindred-ws.service and deploy/kindred-worker.service alongside the existing user/admin units and enable all four services. The supplied Caddyfile expects the WebSocket worker on localhost:8002.

License

This project is licensed under the Business Source License 1.1.

What this means:

  • You can read, fork, and study the code freely
  • You can use it for personal, non-commercial purposes
  • Commercial use (running a competing service, selling it, etc.) requires written permission from the licensor
  • On March 15, 2030, the license automatically converts to MIT, making it fully open source

This license exists to protect the project while keeping the code transparent. See LICENSE.md for full terms.

About

Compatibility-first dating + social platform. Open source, privacy-first. No funny business.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages