2026-05-09 14:15:59 -03:00
2026-05-09 13:47:37 -03:00
2026-05-09 13:41:07 -03:00
2026-05-09 13:37:20 -03:00
2026-05-08 18:18:36 -03:00
2026-05-08 18:18:36 -03:00
2026-05-08 21:32:52 +00:00
2026-05-09 14:00:46 -03:00
2026-05-08 19:18:32 -03:00
2026-05-08 18:18:36 -03:00
2026-05-08 18:18:36 -03:00
2026-05-09 13:41:07 -03:00
2026-05-09 14:15:59 -03:00

The Meme Protocol

Small self-hosted meme gallery matching the stitch_the_meme_protocol desktop mockup direction.

Run locally

npm start

The server listens on http://localhost:8080 by default.

Configuration

  • PORT: HTTP port, default 8080
  • HOST: bind address, default 0.0.0.0
  • DATA_DIR: disk storage root, default ./data
  • SITE_URL: public canonical site URL used for SEO metadata, sitemaps, feeds, and llms.txt
  • SEED_DEMO_MEMES: set to false to disable generated demo memes on first boot
  • ADMIN_TOKEN: secret review URL token. If omitted, one is generated at boot and printed in server logs.
  • OPENAI_API_KEY: enables AI upload moderation. Without it, uploads are queued for admin review.
  • OPENAI_MODERATION_MODEL: moderation vision model, default gpt-4o-mini
  • TRUST_PROXY: set to true when running behind a trusted reverse proxy so upload limits use X-Forwarded-For

Uploads accept PNG and JPEG images. The server rejects files over 5 MB, any image edge over 6000px, and images over 20 million pixels. Accepted uploads are decoded, metadata-stripped, resized so the longest edge is at most 1600px, and stored as WebP. Upload caps are 5 per hour per IP, 10 per day per IP, and 100 globally per day. AI-approved uploads publish immediately; ambiguous uploads are queued for the secret admin review page; likely illegal uploads are rejected immediately.

Files are stored under sharded date/hash paths:

data/
  index/memes.jsonl
  memes/YYYY/MM/DD/aa/bb/<sha256>.<ext>
  meta/YYYY/MM/DD/aa/bb/<sha256>.json

Discovery Metadata

The app serves crawler and answer-engine metadata without adding visible page copy:

  • /robots.txt
  • /sitemap.xml with the home page and approved meme URLs
  • /feed.json
  • /llms.txt
  • /site.webmanifest
  • Open Graph, Twitter card, canonical, and JSON-LD metadata on /

Set SITE_URL in production so canonical URLs use the public domain instead of an internal proxy hostname.

Docker

docker build -t meme-protocol .
docker run --rm -p 8080:8080 -v meme-protocol-data:/data meme-protocol

For production, copy .env.example to .env, set real secrets, then run:

docker compose up -d --build

The included compose file binds the app to 127.0.0.1:18080 on the host so a reverse proxy can publish it without exposing the Node container directly.

Production Build Note

On the production host used for this project, npm registry downloads from inside Docker timed out unless registry.npmjs.org was pinned to a known-good IPv4 address during build. The proven build command is:

sudo docker build \
  --network=host \
  --add-host registry.npmjs.org:104.16.1.34 \
  -t meme-protocol:latest .

Then start with the already-built image:

sudo docker compose up -d --no-build

If the registry IP ever stops working, resolve and test another IPv4 address for registry.npmjs.org, then replace 104.16.1.34 in the build command.

When using a host-mounted data directory, the container writes as UID/GID 10001:10001:

sudo mkdir -p ./data
sudo chown -R 10001:10001 ./data
sudo chmod -R u+rwX,g+rwX,o-rwx ./data
S
Description
Code powering the bitsforfree website
Readme 203 KiB
Languages
JavaScript 77%
CSS 14.7%
HTML 7.5%
Dockerfile 0.8%