No description
  • JavaScript 68.1%
  • EJS 21.3%
  • CSS 9.3%
  • Shell 1.2%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-07-22 23:22:20 +02:00
public (at least try) get notifications and calls working on android 2026-07-22 23:22:20 +02:00
scripts (at least try) get notifications and calls working on android 2026-07-22 23:22:20 +02:00
src (at least try) get notifications and calls working on android 2026-07-22 23:22:20 +02:00
.dockerignore Shrink Docker image from 11.8GB to 392MB 2026-07-21 20:31:29 +02:00
.env.example Implement full plan.md: bug fixes, spec reconciliation, native-client features, SSE, DM API, perf, security 2026-07-16 23:07:09 +02:00
.gitignore Fix all security vulnerabilities 2026-07-05 23:35:11 +02:00
docker-compose.yml Fix docker-compose SESSION_SECRET pass-through for Portainer 2026-07-05 23:42:11 +02:00
Dockerfile Initial commit 2026-07-05 22:02:13 +02:00
package-lock.json Shrink Docker image from 11.8GB to 392MB 2026-07-21 20:31:29 +02:00
package.json Shrink Docker image from 11.8GB to 392MB 2026-07-21 20:31:29 +02:00
plan.md rotateRefreshToken: carry forward 90-day refresh_expires_at on rotated tokens 2026-07-16 23:17:17 +02:00
README.md Initial commit 2026-07-05 22:02:13 +02:00
SECURITY.md Add post deletion with confirmation page 2026-07-05 23:39:58 +02:00

Extrovert

A social network where you can only discover content from friends and friends-of-friends. You cannot see anything from people you aren't connected to.

Features

  • Network-bound discovery. Your feed only contains posts by you, people you follow (your friends), and people they follow (your friends-of-friends). Everyone else is invisible.
  • Posts: text, photos, or videos (uploads stored on disk).
  • Profile pages you can fully customize. Every user can edit the HTML and CSS of their own profile page — no JavaScript allowed (scripts, on* handlers, javascript: URLs, expression(), etc. are stripped on save). Put <!--POSTS--> in your HTML to choose where your posts render.
  • Social actions: follow, like, comment, share, repost.
  • A custom feed algorithm built around how engagement boosts content (see below).

The feed algorithm

Candidates are pulled only from your network (self + friends + friends-of-friends). Each candidate is scored:

score = recencyBase + engagementBoosts + viewerBoosts

Boost rules (constants in src/feed.js):

Action Effect
Follow someone because of a post (the "Follow from post" button) BIG boost to that post (+1000)
Share a post a little more than a like (+60)
Like a post small boost (+30)
Comment and like a post small boost to the post (+30)
Comment without liking no boost to that post at all — instead the poster's other content is boosted, only for the commenter (+50)

Engagement always targets the underlying original content (a repost just surfaces content into a new network and never splits engagement). The same original surfacing both directly and via a repost is de-duplicated to its highest-ranked appearance.

Recency base: 400 * exp(-ageHours / 48), so fresh content starts high and decays.

Getting started

npm install
npm start          # http://localhost:3000

Then sign up, find someone by username on the Discover page, follow them, and your feed fills with their content and their friends' content.

Project layout

src/
  server.js        Express app, session, route mounting
  db.js            SQLite schema + query helpers (node:sqlite)
  network.js       friends / friends-of-friends visibility rules
  feed.js          the feed algorithm (scoring + boost rules)
  sanitize.js      profile HTML/CSS sanitization (no JS)
  auth.js          auth middleware
  routes/          auth, pages, posts, profile, social
  views/           EJS templates
  public/app.css   default styles
scripts/
  test.js          algorithm + sanitization unit tests (npm test)
  smoke.sh         HTTP end-to-end smoke test
  smoke_media.sh   upload + repost smoke test

Notes

  • Storage is SQLite via Node's built-in node:sqlite (no native compile needed).
  • Profile customization is sanitized server-side with sanitize-html plus CSS vector stripping; the sanitized result is also re-checked on render.
  • To reset everything: delete data/extrovert.db and the uploads/ directory.