SkillAgentSearch skills...

Trifecta

educational image sharing website built on a combination of modern C++, web and database technologies

Install / Use

/learn @berthubert/Trifecta
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

trifecta

A simple open source image sharing site, built with a combination of modern C++, database and web technologies. Intended to both be useful and make some points.

Webpage: berthub.eu/articles/trifecta, a blog post detailing "why", blog post about the various technologies used.

Description

Trifecta is a computer program that delivers you a website/web service. Your personal imgur. You can paste or drag images to Trifecta. If you upload an image, a post will be created for it automatically.

A post can contain multiple images. Each image can have a caption, and each post a title.

Posts can be public or not, or have a time limit on their public visibility. As owner of a post you can extend or change this limit.

Users can sign in using an temporary email link, and also reset their password this way. Users need not have an actual password.

Posts in Trifecta get opengraph tags so you get nice previews on social media and in messengers.

Available as docker/podman, rpm, deb and source.

Goals

  • Show how you can build things without hundreds of dependencies
  • Show you can do so self-contained without tons of infrastructure
  • Provide an easy on-ramp for "C++ people" to using modern web technology
    • Using a non-bloated minimal framework (alpine.js)
  • Showcase modern C++ and build tools (Meson)
    • A Rust, Go, whatever, version of the backend is very welcome!
  • Build something that is extremely robust and secure and does not need monthly updates.
    • But still delivers high-end features
  • Actually get an image sharing site for your friends/company
    • Does not provide (moderation) infrastructure for uploads by the public
  • Be a template for other projects

In ~1200 lines of C++ & Javascript, this gets you a safe and secure image sharing site that you could run yourself and hopefully forget about.

Note: the security goals have not yet been achieved, heavy development is ongoing. There are no known problems though.

What is the point?

For one, I'd love to have an 'imgur' just for myself, one that does not monetize me or the viewers of my images. But I also do not want to host a giant web based solution with multiple security issues per year. Or month. I yearn for software like djbdns or qmail that you could trust to not have gaping security holes all the time.

Fundamentally, there is no way to keep a solution with hundreds (or thousands) of dependencies secure. Yet, this is what modern web development has mostly become.

Trifecta is an attempt to create a useful and reliable piece of software that also showcases that it is still possible to write small programs with a much more limited attack surface.

I wrote much more about the why of it all in this post on modern software practices.

Status & Thanks

Development is still ongoing, but usable.

Many thanks are also due to early users, testers and contributors:

  • Ruben d'Arco
  • Roel van der Made
  • Peter van Dijk
  • Bryan Seitz
  • Wander Nauta

While having 700 (indirect) dependencies is not good, benefiting from very good existing software is great:

  • SQLiteWriter for seamless bridge between SQL and JSON, with automated schema generation, SQLite
  • nlohmann-json, great C++ JSON library
  • Alpine.js, a minimalistic Javascript environment
  • {fmt}, excellent string formatting, part of recent C++ standards also
  • cpp-httplib, pretty excellent HTTP library
  • doctest, very nice and fast unit tests
  • argparse, great argument parser

Known problems

  • UI is still somewhat clunky
  • Security is probably not quite yet where it should be
  • The code is still not quite yet "education clean"

Security issues that have been addressed:

  • 2024-01-23: Wander Nauta found that you could make the UI do weird things by passing non-base64 session and post IDs to the website.
  • 2024-01-21: Wander Nauta found out we could crash on logging an error containing a malformed UTF-8 string
  • 2024-01-21: Wander Nauta spotted that adding a linefeed to a content-type would cause the webserver to not set a content-type, allowing you to host random content without a content-type
  • 2024-01-19: Initialization/seeding of random generator was only 32 bits, leading to predictable session id's. Spotted by Josh Simmons. It turns out that the sample C++ code you see everywhere leads to only 32 bits of seed. Solar Designer subsequently pointed out (with proof) that the fix was not good enough, so it has been fixed again.
  • 2024-01-19: Trifecta allows you to upload .SVG files. It turns out that if a user visits a .SVG directly (so not through an <img> element), browsers will execute JavaScript embedded in the file. Spotted by Wander Nauta. Fixed with a Content-Security-Policy, consequences limited by making our cookie HttpOnly.
  • 2024-01-19: Wander Nauta found out that a hostile logged in user could get Trifecta to send email to arbitrary destinations, by injecting control characters in their email address. Trifecta is not designed for hostile users, but this still needed to be fixed.

More low hanging fruit can be found in the GitHub issues list.

Concepts

More about this can be found on the Trifecta web page.

The software consists of a server process, which provides an API for creating users, posts, images etc. It hosts all these in a single sqlite3 database. The server also hosts a few Javascript and HTML files that provide the frontend. To send out password reset/passwordless login emails, it connects to an SMTP server.

To run the software, put it behind a real webserver that does TLS and certificate management for you. Instructions are in the README.

The server configures the sqlite database automatically, there is no need to load a schema. Out of the box, the system is not operational as it has no admin user. If you run the server with --rnd-admin-password it will create an admin user with a randomly generated password for you. If you run it again like that it will only change the password.

Configuration

Configuration is read both from the command line and from the environment:

  • --db-file / TRIFECTA_DB: Path to the sqlite3 database file
  • --html-dir / TRIFECTA_HTML_DIR: Path to the HTML, CSS, SVG and Javascript files
  • --port / TRIFECTA_PORT: Numerical TCP port on which the webserver will listen
  • --local-address / TRIFECTA_LOCAL: IP(v6) address on which the webserver will listen
  • --smtp-server / TRIFECTA_SMTP_SERVER: SMTP server IP:port that allows us to send email
  • --smtp-from / TRIFECTA_MAIL_FROM: FROM and From address for email to be sent
  • --canonical-url / TRIFECTA_CAN_URL: Canonical full URL of the service (for email use)
  • --real-ip-header / TRIFECTA_REAL_IP_HEADER: HTTP header that contains the real IP address of client
  • --trusted-proxy / TRIFECTA_TRUSTED_PROXY: IP address of a proxy we trust to provide real IP address

The command line overrides the environment variables.

There is also a flag:

  • --insecure-cookie: Normally Trifecta will issue 'Secure' cookies, which won't work without TLS (except on localhost). Use this if you want to run without TLS somehow.

To get started:

trifecta --rnd-admin-password

And you should be in business. This creates a random admin password, which it prints for you. It also prints out the URL on which you can contact the service. On first use you'll get some scary looking SQL errors, these go away once you've uploaded your first image.

To do admin things (like create new users), visit /#admin

To take this into production using nginx (for robustness, letsencrypt, TLS etc), try:

upstream backendtrifect {
    server 10.0.0.12:3456 fail_timeout=5s max_fails=3;
}

...

location /trifecta/ {
	rewrite    /trifecta/(.*) /$1 break;
	proxy_pass http://backendtrifect;
	add_header X-Cache-Status $upstream_cache_status;
        client_max_body_size 50M; 
        proxy_set_header X-Real-IP $proxy_protocol_addr;

        add_header X-Cache-Status $upstream_cache_status;
        add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
        add_header X-Frame-Options "SAMEORIGIN" always;
        add_header X-Content-Type-Options "nosniff" always;
        add_header X-XSS-Protection "1; mode=block" always;
        add_header Referrer-Policy "same-origin";
        add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
}

Do know that the default configuration of Trifecta will listen on 127.0.0.1 only, use -l 0.0.0.0 (or TRIFECTA_LOCAL=0.0.0.0) to change this.

It should be obvious, but do not run Trifecta without a front-proxy that provides TLS (except for local testing).

Podman/Docker

You can get the Docker image by pulling berthubert/trifecta from the Docker Hub. There is also a Docker-compose file in example-configs/compose.yaml, through which you can also configure your container.

If running without Docker-compose, this works both for Podman and Docker:

View on GitHub
GitHub Stars178
CategoryData
Updated1mo ago
Forks9

Languages

C++

Security Score

95/100

Audited on Feb 9, 2026

No findings