Files
bitsforfree/README.md
T
smileBeda 48f05bcf8a feat: release meme protocol v1.1.0
Add one-click meme copying, complete viewer actions, fixed-footer pagination, protocol lore, and the Homo Reseticus download chooser.

Install dedicated social and favicon assets, expand local demo data, and synchronize the changelog, documentation, package, and OpenAPI release metadata.
2026-08-14 18:54:37 -03:00

114 lines
5.2 KiB
Markdown

# The Meme Protocol
Small self-hosted meme gallery matching the `stitch_the_meme_protocol` desktop mockup direction.
Current release: `1.1.0`.
The browser feed supports one-click image copy, downloads, shareable meme links, full-image viewing with live view/download counts, a fixed source footer, a two-build Homo Reseticus pet download chooser, and shareable lore at `/lore`.
Homepage and lore social shares use the versioned `1200x630` asset at `/assets/social-preview-v1.png`. Individual meme permalinks continue to use their own meme image.
Browser tabs, bookmarks, Apple home-screen links, and installed web apps use the supplied favicon suite under `/assets/favicon/`; the legacy `/favicon.ico` URL remains available for automatic browser requests.
## Run locally
```sh
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, `llms.txt`, OpenAPI servers, and API skill instructions. Production should use `https://bitsforfree.com`.
- `SEED_DEMO_MEMES`: set to `false` to disable generated demo memes. When enabled, empty and legacy demo-only stores are filled to 18 samples so local pagination can be tested.
- `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`
- `AUTO_APPROVE_MIN_SCORE`: minimum `MEME_CONSENSUS_SCORE` for immediate AI approval, default `80`
- `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. Strong AI-approved uploads publish immediately; ambiguous or low-quality uploads are queued for the secret admin review page; likely illegal uploads are rejected immediately.
Files are stored under sharded date/hash paths:
```text
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:
- `/openapi.json`
- `/meme-api.skill.md`
- `/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 and API discovery URLs use the public domain instead of an internal proxy hostname. The server loads `.env` at startup when present, while real environment variables still take precedence.
## Public API
- `GET /api/memes?page=1&pageSize=12`: lists approved memes only. `pageSize` is capped at `48`.
- `GET /api/memes/<sha256>`: returns public metadata for one approved meme.
- `GET /meme/<sha256>`: opens an approved meme in the viewer and provides its shareable permalink.
- `GET /media/<sha256>`: returns the normalized WebP image for one approved meme.
- `POST /api/memes`: adds one PNG/JPEG upload as `multipart/form-data` with a file field named `meme`.
API uploads use the exact same path as the browser form: persistent IP upload quotas, file size checks, image dimension and pixel checks, metadata-stripping WebP normalization, AI moderation, and admin-review queueing. A published upload returns `201`; a queued upload returns `202` with `moderationReason`; moderation rejection returns `422` with `moderationReason`.
Public API reads are rate-limited per IP and return `429` with `Retry-After` when exceeded. List and metadata responses include `RateLimit-*` headers and a `rateLimit` object.
## Docker
```sh
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:
```sh
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:
```sh
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:
```sh
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`:
```sh
sudo mkdir -p ./data
sudo chown -R 10001:10001 ./data
sudo chmod -R u+rwX,g+rwX,o-rwx ./data
```