SendRepute Campaigns ships a compiled runtime you can self host
Self-hosted SendRepute Campaigns compiled runtime distribution
At a glance
- What is it?
- This repository distributes the compiled SendRepute Campaigns runtime rather than the full source monorepo, with a setup script that installs Docker, Postgres and a Caddy proxy on a fresh Ubuntu server. Updates are checked and checksummed but never installed for you, and the access mode you choose at setup decides how exposed the app is.
- Who is it for?
- SendRepute Campaigns fits a team that wants its audience data, templates and delivery to run on its own server under its own control, and that is prepared to run a small Docker stack with a Postgres database, a reverse proxy and real DNS. It is the wrong choice if you want a hosted builder, if you cannot own a domain name and manage TLS, or if you need the application to update itself, since it deliberately never installs or executes a download.
- 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 9 days ago.
- What is it written in?
- Mainly Shell, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
You get a compiled runtime, not the source monorepo
The first thing the readme says is that this repository distributes the compiled runtime, not the full source monorepo. That is the single most important fact for anyone evaluating the project.
What the product is, in the readme's own terms, is self hosted audiences, campaigns, templates, automations and delivery from your own server. The homepage points at a campaigns page with a demo mode, and the readme points out that the interactive demo has no real messages, no hosted builders and no paid results.
The repository itself is a runtime distribution. The top level holds setup.sh and install.sh as the entry points, start-https.sh, a compose.yaml, a Caddyfile, a Dockerfile, a public directory for static assets, a packages directory with the server, delivery, bridge and node packages, a downloads directory for release archives, a docs directory, and backup.sh with its systemd unit and timer examples. The package metadata names the workspace sendrepute-campaigns-runtime at version 0.1.44, marked private, with pnpm style workspaces over packages.
It is also a repository with a wide international audience in mind. The readme is published in fourteen languages, from English through Arabic, French, Russian, Ukrainian, Hindi, German, Spanish, Italian, Portuguese, Indonesian, Turkish, Simplified Chinese and Vietnamese, which is a reasonable signal about who the intended operator is.
One script on a fresh Ubuntu server
The quickstart is aimed at a server with nothing on it yet, and it is deliberately explicit about that.
sudo apt-get update
sudo apt-get install -y git
git clone https://github.com/sendrepute/sendrepute-campaigns.git
cd sendrepute-campaigns
./setup.shThe script asks which access mode to use, and it reuses a working Docker and Compose installation if it finds one. On a fresh supported Ubuntu server without Docker, it requests consent before installing Ubuntu's packages. Two things it explicitly does not do are worth noting: it does not remove Snap Docker, and it does not bypass AppArmor.
There is also a firm instruction not to clone over an existing installation. If you already have Campaigns on the box, the update path is a different procedure, and the readme says to back up first and follow the migration steps in the installation documentation before changing a live installation.
The Ubuntu versions named are 24.04 and 26.04, and the compile step is not part of this story: the runtime arrives as a built artefact, so what you are really installing is a container image plus a Postgres database plus, optionally, a proxy in front of it.
Three access modes, and only one of them belongs in production
Setup asks you to choose how the app is reached, and the three options are not equally acceptable.
The recommended one is an HTTPS domain. Setup starts the included Caddy proxy, and you enter a subdomain you control, with no scheme, no port and no path. You then point that hostname's A record at the server, allow TCP 80 and 443, and check the certificate from outside. The AAAA record is only for servers with working IPv6.
The second is a local or SSH tunnel, where the app binds to the loopback interface of the VPS and you open it through a trusted tunnel from your own computer. The readme is careful about the distinction, since the localhost on your laptop is not the localhost on the server.
The third is public IP with plain HTTP, and here the language is blunt: you explicitly consent to unencrypted access on port 8080, where passwords, tokens and sessions can be intercepted, and it is not for a secure production deployment.
That is the whole security posture in three sentences, and it is worth reading before the script asks you the question rather than after. The compose file backs it up, since the published port binds to 127.0.0.1 by default through CAMPAIGNS_BIND_ADDRESS, and the environment file comments say to set CAMPAIGNS_TRUST_PROXY only behind the bundled Caddy proxy.
The setup token is the one manual step
After setup, the app prints an installation page, and for a new owner one thing has to be done by hand: the one-time token.
You read it out of the running container.
sudo docker compose exec campaigns cat /var/lib/sendrepute-campaigns/installer-tokenThe readme says to omit sudo if your Docker account does not require it. You copy the output and paste it into the Setup token field on the printed installation page, then fill in the owner details and the corresponding Public URL, which has to end in /campaigns/, and complete SendRepute activation and owner setup in the wizard.
Two cautions follow. Setup does not print the token automatically, so this is not optional for a new owner, and the token, the .env file and the logs are to be kept private, with the token never put in a URL or shared. An already installed workspace does not need another setup token.
The readme also draws a line under what the ready message means. CAMPAIGNS READY FOR OWNER SETUP means the app is healthy inside its container, not that owner setup is complete or that public HTTPS has been verified. For HTTPS you have to verify the printed installation page from another network, and it must have a valid, trusted certificate with no browser warnings. If the certificate is pending or invalid, the instruction is not to enter any secrets.
Switching to HTTPS without reinstalling
Moving an existing installation to HTTPS is a separate command, run in the existing project directory, and the readme is explicit that a fresh clone is the wrong move here.
./setup.sh --mode https --host campaigns.sendrepute.comThe steps around it matter as much as the command. Back up first, point your actual hostname at the VPS, free or open TCP 80 and 443, and confirm the DNS resolves. The readme warns that campaign and campaigns are different DNS names, which is the kind of detail that costs an afternoon if you guess.
You confirm the exposure change when asked, setup starts Caddy, and it does not rewrite the installed workspace's saved Public URL, so after verifying HTTPS externally you change that URL in Workspace Settings to the https form with your host and the /campaigns/ path. The readme also names the Cloudflare setting to use, Full (strict), and says never Flexible.
The proxy itself is part of the installation rather than something you configure by hand, and the same mechanism handles extra names later. The Domains page lets you create a hostname challenge, add the A or AAAA record and the exact displayed TXT ownership record, and then check DNS and verify. A verified hostname is added to the same managed proxy, and the primary certificate is reused when its subject alternative names cover it, otherwise a new certificate is requested. No per domain shell commands or proxy edits are needed, and the primary installation hostname and certificate settings stay as they are.
Updates are detected and checked, never applied
The update story in this project is unusual, and it is the part most worth reading carefully if you plan to run this in production.
Administrators can check for stable releases in Workspace Settings. The preferred source is the official versioned release archive and its SHA-256 checksum attachments. If those expected attachments are missing, the checker can instead resolve the same stable release tag in the official repository to its immutable commit and validate that commit's bounded downloads/ checksum manifest, and the repository download links are pinned to that commit rather than to a movable tag or branch. Invalid or duplicate expected attachments never permit that fallback.
What the application does with the result is narrow. Settings displays an update notice, download and checksum links, and the documented manual server update command for an existing Git clone, and it never installs, extracts or executes a download. The operator performs the upgrade and the restart, and the readme says to back up the installation first. Archive installations have their own upgrade path and have to verify the downloaded bytes against the checksums.
The failure handling is written with the same care. GitHub failures, absent or malformed publication metadata, and unknown installed versions are reported as unavailable, never as up to date. New release archives include their installed version metadata, and older installations without that metadata cannot claim to be current. Release publishing and the checksums are separate operator steps, not something the application performs.
The practical effect is that upgrading is a manual, verifiable operation rather than an automatic one, which is the right trade for something that runs on your own server, and also the reason you need to read the operator procedure rather than expecting a button.
A hardened container, a real database, and no stated licence
The compose file shows how the runtime is put together, and it is tidier than most self hosted stacks.
services:
postgres:
image: postgres:17.4-bookworm
restart: unless-stopped
environment:
POSTGRES_DB: campaigns
POSTGRES_USER: campaigns
POSTGRES_PASSWORD: ${CAMPAIGNS_POSTGRES_PASSWORD:?Run ./install.sh first}The password is not a default, and the compose file refuses to start without one. The application service is built from the Dockerfile, runs read only, uses the no-new-privileges option, gets a 64 megabyte tmpfs for /tmp, and waits for Postgres to report healthy before it starts. There is a separate init service that fixes ownership of the shared site volume, and the application reaches the database through a DATABASE_URL pointing at the postgres service.
The Dockerfile is a two stage build on node:22.14.0-bookworm-slim, with the dependency layer installed with npm ci, then a runtime image that creates a system user with a fixed uid of 10001, runs as that user, exposes port 8080, and has a health check that calls the campaigns status endpoint.
Two things to note before you deploy. The environment file says in its first line that copying it is not the standard setup path and that you should run install.sh instead, which is a small sign of how the operator workflow is meant to go. And the licence for this repository is not stated in the project listing, so the terms are whatever the LICENSE file in the repository says, which is worth reading before you put audience data behind it. The activity record is healthy: the repository is not archived, the last push was on 2026-10-14, and the release tags show v0.1.44 on 2026-10-04 with two releases the day before.
Editorial conclusion
SendRepute Campaigns fits a team that wants its audience data, templates and delivery to run on its own server under its own control, and that is prepared to run a small Docker stack with a Postgres database, a reverse proxy and real DNS. It is the wrong choice if you want a hosted builder, if you cannot own a domain name and manage TLS, or if you need the application to update itself, since it deliberately never installs or executes a download. Check first which access mode you will use, that the Postgres password is set through the install path rather than by copying the example environment file, and that you understand what the compiled runtime does and does not include before you plan a deployment around it.
Frequently asked questions
What does the sendrepute-campaigns repository contain?
The compiled runtime distribution rather than the full source monorepo. The repository holds setup.sh and install.sh, a compose.yaml, a Caddyfile, a Dockerfile, the packages directory, public assets, downloads for release archives and the docs.
How do I install SendRepute Campaigns on a fresh Ubuntu server?
Install git, clone the repository, and run ./setup.sh from the project directory. The script asks which access mode to use, reuses a working Docker and Compose setup, and requests consent before installing Ubuntu's packages on a server without Docker.
Why does setup ask for a one-time token?
For a new owner the token is required, not optional. You read it with docker compose exec campaigns cat /var/lib/sendrepute-campaigns/installer-token, paste it into the Setup token field on the printed installation page, and never put it in a URL or share it.
Does the app update itself?
No. Workspace Settings detects the available stable release and shows download and checksum links plus the manual server update command, but it never installs, extracts or executes a download, and it reports failures as unavailable rather than up to date. The operator performs the upgrade and the restart.
Official sources
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.
[](https://hysenlabs.com/projects/sendrepute-sendrepute-campaigns)Was this useful?