Open-source project
BlitzKraft/saythanks.io avatar
BlitzKraft/saythanks.io

saythanks.io needs Auth0, Postgres and three env files, which is the cost of a thank you button

Spreading Thankfulness in Open Source.

1,316 stars197 forksPythonLicense varies

At a glance

What is it?
saythanks.io gives open source maintainers a button that turns into a private inbox of thank you notes, delivered by MailerSend. It is a single Flask application with Auth0 for credentials and Postgres for storage, and its own repository carries several files that show how much of a small service still needs configuring.
Who is it for?
saythanks.io fits a maintainer who wants a visible counterweight to the issue tracker and wants strangers to be able to say thanks without an account, since the whole value is that it is one embeddable link. Do not adopt it as infrastructure you need to run yourself without reading the setup documents, because the configuration surface is three environment files plus two hosted services, and the authentication work is described as OAuth2 in progress.
Can I use it commercially?
Not without permission. GitHub finds no licence file in the repository, and without a licence all rights are reserved by default: you may read the code but not reuse it. Check the README, or ask the authors, before using it.
Is it still maintained?
Yes. The repository last received commits 8 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on November 7, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • BlitzKraft/saythanks.io1,316 ★ · +1
Star history
Repository2026-10-172026-11-07
BlitzKraft/saythanks.io1.3k1.3k

1 Stars in the last 21 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

The service is small and the surrounding architecture is not

The concept is one paragraph long. The site provides a button or link for use by open source projects, to encourage users to send a simple thank you note to the creator or creators of that project, and the button can be added to READMEs and project documentation. The author then gets a private inbox filled with very small thoughtful messages from the people who use the software they toil over.

The implementation concepts section lists what the service actually does: send an email when a new message of thankfulness is submitted with CSRF enabled, and provide an inbox page for each user or project with simple aggregation of messages, described as private.

So the flow is: a visitor opens a link, submits a short note, an email goes out to the maintainer, and the message is also stored for the inbox. CSRF protection on the submission is called out explicitly in the features list as one of the recent improvements, alongside message inbox aggregation for users and projects.

That is a small amount of application logic. The architecture list is where the weight sits: Flask for API and frontend as a single application, Auth0 for credential storage, hosting, and CloudFlare for SSL termination.

GitHub account creation is supported, as are passwordless email accounts. The credentials work is described as Auth0 done with OAuth2 in progress, and the recent improvements list repeats that authentication work is ongoing rather than finished.

Email delivery moved from SendGrid to MailerSend, pinned exactly

The first recent improvement is a migration, and the reason given is deliverability.

Transactional email delivery moved from SendGrid to MailerSend, described as improved deliverability, modern API support and more reliable service, addressing recent deliverability issues. The dependency is pinned to a single version in the requirements file:

code
mailersend==0.6.0

That pin is worth noting because the Python client for a hosted email API changes shape across versions, and a service whose entire function depends on an email arriving has a strong reason not to float the dependency.

The README links the library's documentation for sending a template-based email, which is the mechanism this service presumably uses, since a thank you note delivered as a template renders more consistently than one assembled from raw HTML.

The migration is also a signal about the service's failure mode. Nothing about the application breaks if the mail provider's API changes shape in an incompatible way, but every submitted note stops being delivered, and the maintainer's inbox quietly stops filling. That is a failure with no error surface, which is why the deliverability argument and the exact version pin are both in the README.

The hosted side moved too: hosting migrated to KGiSL from Heroku, with CloudFlare continuing for SSL termination. The architecture section still lists Heroku for hosting with a done marker and then notes it is now hosted at KGiSL, so the README carries the history rather than having cleaned it up.

Docker Compose brings Postgres, pgAdmin and three env files

The compose file is the clearest picture of the local development setup, and it is a three-service stack.

yaml
services:

  site:
    build: .
    ports:
      - 5000:5000
    env_file:
      - ./conf/site.env
    volumes:
      - ./:/saythanks
    depends_on:
      - db

  db:
    image: postgres
    container_name: local_pgdb
    restart: always
    ports:
      - "5432:5432"
    env_file:
      - ./conf/db.env
    volumes:
      - local_pgdata:/var/lib/postgresql/data
      - ./saythanks/sqls/schema.sql:/docker-entrypoint-initdb.d/schema.sql
    networks:
      - saythanks

The `site` service builds from the local Dockerfile, maps port 5000, reads `./conf/site.env`, and bind-mounts the working directory into `/saythanks` so edits take effect without rebuilding.

The `db` service is Postgres, with the schema mounted into `docker-entrypoint-initdb.d/schema.sql`, which is the mechanism by which the database is initialised on first run from a file tracked in the repository. That means the schema is versioned as SQL rather than as migrations, which is simpler and less flexible.

A third service, `pgadmin4_container` from `dpage/pgadmin4`, gives a web database client on port 8888. It is a development convenience rather than a service the application needs.

Two named volumes persist data, `local_pgdata` and `pgadmin-data`, and the services share a network called `saythanks`.

So bringing this up locally requires creating three env files, `site.env`, `db.env` and `pgadmin.env`, under a `conf/` directory. There is a `conf_template/` directory at the repository root and a `sample.env`, which are presumably the starting points, and the top-level entries include `DOCKER_SETUP.md` and `docs/README.md` for the setup instructions the README points to.

The Dockerfile is Ubuntu 18.04, and the requirements file is a decade of Python

The Dockerfile in the repository root is the oldest thing in it and the most likely to surprise you.

dockerfile
FROM ubuntu:18.04

# Installing python
RUN apt-get update
RUN apt-get install -y libpq-dev 
RUN apt-get install -y python3-dev 
RUN apt-get install -y python3-pip 
RUN apt-get clean

The base image is `ubuntu:18.04`. The Dockerfile runs `apt-get update`, installs the Python build dependencies and then runs `apt-get clean` within one build instruction.

The Dockerfile installs `libpq-dev`, `python3-dev` and `python3-pip`. There is also a `runtime.txt` and a `Procfile` at the repository root.

The requirements file pins `flask<2.3` and `auth0-python<=2`, and also lists packages including `raven`, `python-http-client`, `flask_common`, `contextlib2`, `ordereddict`, `six`, `lxml_html_clean`, `pendulum`, `maya` and `dateparser`.

The Makefile generates a target per file, and the tooling is duplicated

The Makefile is built by globbing the repository, which is unusual and worth understanding before you add a file.

Four target lists are computed at parse time from wildcards: Jinja templates from `saythanks/templates/*.htm.j2`, and Python files from `saythanks/*.py`, `t.py` and `tests/*.py`. Each name in those lists becomes a make target.

make
$(TEMPLATE_TARGETS):
	pipenv run djlint saythanks/templates/[email protected] --lint
make
$(PYTHON_SAYTHANKS_TARGETS):
	pipenv run flake8 saythanks/[email protected]

This creates a per-file linting interface from the filenames matched by the Makefile.

The all-files targets are `lint`, which runs flake8 across `saythanks` excluding `.tox`, and `test`, which runs pytest with verbose and colour output against `tests/`.

There is a small inconsistency in the tooling. Formatting has two targets, `djlint-reformat` and `djlint-reformat-win`, where the first invokes `.venv/bin/djlint` directly with a two-space indent and the second invokes `djlint` from the path, and the aliases `reformat` and `reformat2` point at them respectively. The Windows variant exists because the first target hardcodes a virtualenv path.

Two targets are commented out: `#init:` with `pipenv install --dev`, and `#run:` with `pipenv run python saythanks/app.py`. Both are disabled, which means the documented entry point is not the one in the Makefile. The Dockerfile instead runs `python3 t.py`, so `t.py` at the root is the application entry point.

Cleanup targets remove `.pyc`, `.pyo`, editor backup files and `__pycache__`, and remove `build/`, `dist/` and `.eggs/`.

Small touches, maintenance state and who this is for

A few features in the recent improvements list are worth picking out because they describe who the service is actually for.

Localized placeholder names adds an option to generate Indian-style placeholder names in the note submission form alongside the existing American-style names, via a `?name_style=indian` URL option. That is a small feature with a clear audience: someone writing on behalf of many people, or in a context where a Western placeholder name reads as fake. It implies a user base outside the Anglophone open source mainstream.

A markdown editor was added for writing notes, Toast UI Editor, with live preview and enhanced formatting. The topic list includes `inbox`, which confirms the inbox is the core artefact.

Other entries are maintenance rather than feature: the codebase has been prettified and refactored for maintainability, unnecessary files and legacy configs were cleaned up, CSRF protection and inbox aggregation were improved, and the UI and UX received updates including button colours.

The intended collaborators section lists four people by name and role, two of them on Python and JavaScript, one on logo and graphic design and one on frontend design, with the fourth carrying the placeholder Your Name Here Instead in the attribution.

Maintenance state: the repository is not archived, the last push was on 2026-10-08, and the listed releases are 2.2 as the Astra Release on 2026-09-09, 2.0 as the Fourth Release on 2026-08-28 and 1.5 as the Third Release on 2026-08-11. The listed releases run from 11 August to 9 September 2026.

The licence field is recorded as unknown and there is no licence file among the repository entries, which matters if you want to fork and self-host the service rather than just embed the button. The repository does carry a `SECURITY.md`, a `CONTRIBUTING.md` and a pull request template.

Editorial conclusion

saythanks.io fits a maintainer who wants a visible counterweight to the issue tracker and wants strangers to be able to say thanks without an account, since the whole value is that it is one embeddable link. Do not adopt it as infrastructure you need to run yourself without reading the setup documents, because the configuration surface is three environment files plus two hosted services, and the authentication work is described as OAuth2 in progress. Verify first what the privacy claim means for your users, since the inbox is private but the messages are delivered as email, and email is a channel you do not control the retention of.

Frequently asked questions

What does saythanks.io do?

It gives open source projects a button or link that users can add to their README, which lets a visitor send a short thank you note to the project's creators. The maintainer gets an email and a private inbox page aggregating the messages.

How do I run saythanks.io locally with Docker?

The compose file defines a site service on port 5000 reading `./conf/site.env`, a Postgres service on 5432 reading `./conf/db.env` with the schema mounted into docker-entrypoint-initdb.d, and a pgAdmin service on 8888. You need to create the three env files, with `conf_template/` and `sample.env` as starting points.

Which email provider does saythanks.io use?

MailerSend, pinned at version 0.6.0 in the requirements file. The README records a migration from SendGrid to MailerSend for improved deliverability, modern API support and reliability after deliverability issues.

What stack does saythanks.io use?

Flask as a single application for API and frontend, Auth0 for credential storage with OAuth2 in progress, Postgres for storage via SQLAlchemy and psycopg2, and CloudFlare for SSL termination. It was hosted on Heroku and is now hosted at KGiSL.

How do I run the tests for saythanks.io?

With `make test`, which runs `pipenv run pytest --verbose --color=yes tests/`. Linting the whole project is `make lint`, and the Makefile also generates a per-file lint target for every Python file and Jinja template in the repository.

What license is saythanks.io released under?

The repository's licence field is recorded as unknown and there is no licence file among the repository entries, so check with the maintainers before forking or self-hosting. The repository does include a SECURITY.md and a CONTRIBUTING.md.

Official sources

  1. BlitzKraft/saythanks.io on GitHub
  2. Issues
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/blitzkraft-saythanks-io.svg)](https://hysenlabs.com/projects/blitzkraft-saythanks-io)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/blitzkraft-saythanks-io.svg)](https://hysenlabs.com/projects/blitzkraft-saythanks-io)

Was this useful?