Skip to content

Repository files navigation

son collection

son collection

Every son, collected. A gallery dedicated to the “Son 😭😭😭😭😭” meme and its endless variants: pictures, GIFs and clips, in one place.
soncollection.com · clips · upload · leaderboard

Rust Cloudflare Uploads Video


Sonion. Capri-Son. Dy-Son. Sonflower. Sontato. If it has a son in it, it belongs here.

Uploads are free, publish instantly, and are displayed without public attribution unless you sign in.

What is this?

A community gallery, and an attempt at the most complete collection of son memes there is. Find a son in the wild, upload it, and it gets a page of its own with a link that unfurls properly in Discord, Slack, iMessage and X, an embed code that works on any site, and a spot in the grid.

Every son is one of three things:

  • an image (PNG, JPG, WEBP, up to 12 MB), re-encoded on the way in;
  • a GIF (up to 12 MB), stored exactly as uploaded so it keeps animating;
  • a clip (MP4 or WEBM, up to 60 MB), stored as uploaded and played inline.

Once the collection is large enough, the plan is to publish a cleaned and deduplicated dataset on Hugging Face so researchers, artists, and AI agents can study the format -- and perhaps generate even more sons.

Upload rules

  • No NSFW, illegal, hateful, violent, harassing or otherwise harmful content.
  • Sons only. The original, its edits and reactions, clips, and the wordplay that grows out of them -- anything with a good-faith son in it.
  • Upload originals. Crop out screenshots, browser chrome, social-media interfaces, notifications and unrelated borders first.
  • Check for duplicates. Exact duplicates are refused automatically; near-duplicates are on you. We do not need a billion copies of the same son floating around.
  • Only upload what you have the right to share. See /tos and /dmca.
  • No personal information, and nothing that violates someone's privacy.

Uploads may be removed at any time if they break these rules or create legal, safety, privacy, or moderation concerns.

Importing from a link

The upload page takes a link as readily as a file: paste one into the box (or anywhere on the page) and the server fetches the media at original quality and runs it through the same pipeline as an upload. What works, and how:

  • X / Twitter posts, through the same public endpoint that powers embedded tweets. The highest-bitrate MP4, or the photo at full resolution. Pure Rust.
  • Any page with Open Graph or Twitter Card tags, and any bare image or video URL. Pure Rust.
  • YouTube, TikTok, Reddit, Vimeo, Twitch clips, Threads, Bluesky, and Instagram and Facebook when they cooperate: yt-dlp, run as a subprocess and pointed only at that allowlist of hosts, with ffmpeg merging the streams. Instagram reels and many Facebook videos are shown only to a signed-in account; IMPORT_COOKIES_FILE lets the importer be one (see .env.example).

Every fetch the server makes itself goes through one guarded client that resolves the host, refuses anything not publicly routable and pins the connection to the checked addresses. The source page is recorded on the son and shown as a "via" link, which is also where a takedown request looks first.

Moderation

Nothing is screened before it appears and nothing waits for approval: an upload is live the moment it is stored. Anyone signed in can report a son; three reports hide it automatically, and every report lands in /admin for a human, who can hide or delete anything. That is the mechanism, not a fallback for one.

Contributing

The best way to contribute is simple:

  1. Find sons in the wild.
  2. Check whether they are already in the collection.
  3. Crop and clean the image, or trim the clip.
  4. Upload it.

Code contributions are welcome too. The repository is public, and anyone brave enough to improve the infrastructure is appreciated.

AI-generated sons

Have time and a questionable idea? Generate a son with AI and submit it. Tasteful AI slop is welcome -- label it clearly in the title and follow the same rules.

Privacy

Uploads are shown without public attribution (first name only if you sign in), but this does not mean complete technical anonymity: the service and its infrastructure process IP addresses, timestamps and request logs for security and abuse prevention. Images are re-encoded, which strips camera metadata; GIFs and videos are stored as uploaded, so strip your own if it matters.

Do not upload anything that could identify you or another person.

Rights and licensing

The license covering this repository's source code does not apply to user-uploaded media. By uploading, you confirm that:

  • you created it, have permission to share it, or reasonably believe its use is lawful;
  • it does not violate another person's copyright, trademark, privacy, or other rights; and
  • you grant the project permission to store, display, process, moderate and distribute it as part of the site, its API and its embeds, and to include it in downloadable research datasets.

Commercial use

This project and its dataset are intended for personal, cultural, archival, and research use.

Commercial use of the dataset or hosted collection is not permitted without prior written permission.

See the repository license and dataset terms for the exact conditions.

Disclaimer

This is an experimental community archive. Availability is not guaranteed, uploads may be removed without notice, and the collection may contain material submitted by third parties.

The maintainers do not endorse every upload.


Technical notes — running it, developing it, deploying it

Pure Rust, including the UI: Leptos 0.8 (SSR + hydration) on Axum, Tailwind for styling, Cloudflare D1 for data and R2 for media. There is no sidecar, no Python, no browser automation and no model: the app is one binary, and the production image is that binary plus cloudflared and the link importer's three pinned helpers (yt-dlp, deno, ffmpeg).

This site and kirkaversary share one codebase with different identities; a fix in one belongs in the other.

No local database. Dev talks to the same D1 and R2 as production — there is no sqlite fallback to drift from. Clean up anything you upload while testing.

Video is never decoded on the server for an upload. The browser draws a frame of a picked clip onto a canvas and uploads it as the poster; the server sniffs the container, hashes the bytes, makes the thumbnail from the poster, and stores the file as it arrived. ffmpeg is only ever run for a link import, where no browser is in the loop.

Run it locally

cp .env.example .env          # fill in the Cloudflare values; the rest degrade
                              # gracefully when unset
npm install                   # Tailwind only; the image builds without Node
cargo install cargo-leptos --locked
cargo leptos watch            # http://127.0.0.1:3100

watch, not serveserve doesn't watch anything. Kill the old process first, or you'll review the previous build.

The gate, before you push

CI takes a while and the image build is last, so a mistake there costs many minutes to learn something these say in three:

cargo fmt --all -- --check
cargo clippy --no-default-features --features ssr --all-targets -- -D warnings
cargo clippy --no-default-features --features hydrate --target wasm32-unknown-unknown -- -D warnings
cargo test  --no-default-features --features ssr
scripts/audit-secrets.sh      # no credential in history, files or build output

Both feature sets, always: ssr and hydrate compile disjoint code from the same files, so one passing tells you nothing about the other. git config core.hooksPath .githooks runs all of it on every push.

Infrastructure, from nothing

Three scripts, three jobs, each idempotent and each re-runnable on its own:

scripts/cloudflare-provision.sh > secrets.env   # D1, R2 + media domain, scoped
                                                # tokens, tunnel + DNS, www
                                                # redirect, Web Analytics
scripts/d1-migrate.sh                           # every migration, in order
scripts/cloudflare-setup.sh --apply             # zone settings that matter here
scripts/google-search-console.py                # verify the domain, submit the sitemap

secrets.env is one NAME=value line per GitHub Actions secret the deploy job reads: set each as a repository secret of the same name. It is gitignored. Never commit it.

Deploy

One image, one container: the site and cloudflared under supervisor (Dockerfile, deploy/supervisord.conf, docker-compose.yml). Push to main and GitHub Actions builds it, pushes it to GHCR by digest, joins the tailnet as an ephemeral tag:ci node and runs docker compose up on the host over Tailscale SSH. There is nothing to click.

Every credential is a GitHub Actions secret, injected as runtime environment — none is a build argument, none is in an image layer, and there is no .env on the server. scripts/audit-secrets.sh keeps that a fact rather than an intention: it scans every object in git history, every tracked file, the compiled binary, the wasm every visitor downloads, and (with --image) every layer of the built image.

The container publishes no port. cloudflared reaches the app on loopback inside it, so the host needs no inbound rule for web traffic.

Search

Everything here is built to be found:

  • sitemap.xml with the Google Images and Google Video extensions, lastmod on every entry
  • ImageObject / VideoObject, BreadcrumbList and WebSite + SearchAction JSON-LD
  • Open Graph (with og:video for clips), Twitter cards, oEmbed discovery on every son page
  • /?view=clips as its own canonical, indexable page
  • llms.txt for answer engines; robots.txt that blocks nothing a crawler should read
  • IndexNow pings (Bing, Yandex, Seznam, Naver) the moment a son publishes, when INDEXNOW_KEY is set
  • A Cloudflare Turnstile captcha in front of every upload and link import, when TURNSTILE_SITE_KEY and TURNSTILE_SECRET are set; the server verifies each token with Cloudflare before it reads the file
  • a 301 from www. to the apex at the edge

Before you change anything

CLAUDE.md is the list of things that have already cost real time in this codebase and the sister site that shares it. It is not documentation of how the code works; it is a list of traps.

Made for the Sons.

About

A son meme collection repo

Topics

Resources

Security policy

Stars

1 star

Watchers

0 watching

Forks

Used by

Contributors

Languages