Skip to content
designAtHotlyPublic

About

Self-hosted Hotly for exactly one creator.

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

8 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Hotly open-source

Self-hosted Hotly for exactly one creator. Apache License 2.0. Guests buy a one-time note or a weekly Penpal subscription; the creator replies from an inbox, including optional image/video (free or paid unlock). /dome is the retained community. Payments go to the operator’s Stripe account.

Browsers land on /penpal. The public origin is Caddy (/ + /api), not :3000 or :8080.

Prerequisites

You supply the machine, DNS, and accounts. This project does not create a VPS, a domain, Docker, or Firebase / Stripe / SendGrid. Operators build images from source. There are no published prebuilt images.

Linux VPS

Production is a Linux virtual machine you can SSH into. Windows is not supported (not as a server, and local Docker Desktop on Windows is not a supported host). Try the stack locally on Linux or macOS.

If you build images on the box (the documented path): 2 vCPU and at least 4 GB RAM. Typical cloud images have no swap. A 2 GB machine is too tight for docker compose up --build. Put the VM in a region close to the creator and the guests you expect — every page and upload is a round trip to this host.

Enable the provider’s automatic backups or disk snapshots. That is not enough by itself: PostgreSQL and media live in Docker volumes on that disk. Still run docs/operations.md on a schedule and copy .backup/ off the VPS. Losing the machine without an off-box copy means losing conversations and uploads.

Any generic Docker-capable Linux VPS works. A DigitalOcean Basic 2 vCPU / 4 GB class machine is enough. Similar: Hetzner Cloud, Linode, Vultr, OVHcloud, AWS Lightsail, Google Compute Engine. You need a VM. Render, Railway, Fly, and other single-service PaaS are not this Compose stack.

Docker Engine and Compose v2

On that machine you need both:

  • Docker Engine (docker info reaches a running daemon)
  • the Compose v2 plugin (docker compose version, with a space)

The old docker-compose (hyphen) binary is not enough. The installer will not install Docker or Compose for you.

On an Ubuntu/Debian VPS as root (same command we used; Engine plus Compose v2):

curl -fsSL https://get.docker.com | sh
systemctl enable --now docker
docker info
docker compose version

Not for macOS. Official packages: Install Docker Engine. macOS local: OrbStack or Docker Desktop.

Domain and DNS

A hostname at any registrar, with access to create A/AAAA records. A raw public IP is a smoke host, not the origin guests, Google, Stripe, or recovery mail should use.

Moving the zone to Cloudflare is recommended. Keep the record DNS only (grey cloud) until Caddy has a Let’s Encrypt certificate; orange-cloud proxy hides the origin from Let’s Encrypt.

Accounts

Operator-owned Firebase (Google sign-in), Stripe, and SendGrid. The stack boots if those keys are empty; the matching pages then say the provider is not configured.

Host size

Bare minimum for a public VPS that builds images on the box: 2 vCPU and 4 GB RAM. Typical cloud images have no swap.

Idle Compose is a few hundred MiB. On a 4 GB Ubuntu host, docker compose up --build can peak around 2.7 GB used. A 2 GB machine is too tight for on-box Next.js + Go builds.

Disk needs room for the OS, images, PostgreSQL, and media. Media growth is the long-term limit, not RAM. Measured breakdown: docs/operations.md.

Run it locally

Linux or macOS with Docker Engine and Compose v2. Installer (fail-fast if Docker is missing; does not install Docker or Bun):

./scripts/install.sh --local

Skip providers or Compose while testing: --skip-firebase --skip-stripe --skip-sendgrid --skip-up --yes. Below 2 vCPU / 4 GB Docker RAM the installer stops; --skip-spec overrides that. The "System (paste this in a GitHub issue)" block is for bug reports.

Or by hand:

cp .env.example .env
# Set CREATOR_EMAIL. Replace SESSION_SECRET, RECOVERY_TOKEN_SECRET, and Garage keys.
# Leave Firebase, Stripe, and SendGrid empty until you have those credentials.
docker compose up --build

Open http://localhost. Health: GET /api/health.

Compose starts PostgreSQL 18, runs db/migrations before the API, then the Go API, Next.js, Garage (private S3), and Caddy.

Routes

Path Who
/ Redirects to /penpal
/penpal Public landing: creator, USD prices, Checkout, “Powered by Hotly.”
/chat Paid guest conversation (HTTP-only cookie after Checkout or recovery)
/dome, /dome/:promptSlug Community; #note-<id> is a stable note fragment
/creator, /creator/inbox, /creator/dome, /creator/settings Configured Firebase email only
/auth/firebase Google sign-in
/auth/chat-recovery Guest recovery request and consume

USD only. Defaults: one-time notes $5 / 250, $10 / 500, $15 / 1,000 characters (ONE_TIME_PRICE_CENTS, ONE_TIME_PRICE_500_CENTS, ONE_TIME_PRICE_1000_CENTS); Custom must be at least $1 above the 1,000-character price; weekly Penpal $15 / 2,000 characters (WEEKLY_PRICE_CENTS=1500). Named-tier Checkout ignores browser amount and currency.

Apps

  • apps/web — Next.js UI (Bun). Same-origin /api fetches with cookies. Favicon, apple-touch, android-chrome, wordmarks, and site.webmanifest are in apps/web/public.
  • apps/server — Go API (Chi, pgx, sqlc). Automated tests live here only.
  • emails — React Email sources for the three SendGrid bodies. bun run emails:render, then rebuild server. Details: emails/README.md.

There is no frontend test suite. UI is checked with bun run check-types / bun run build and by using the app.

On GitHub, pull requests, main, and v*.*.* tags run Go tests, bun run check-types, and docker compose config. Cutting a version tag publishes a GitHub Release after those checks pass.

Local apps without Compose (API still needs Postgres and the env):

bun run dev

Production

Bring DNS up before the first public Compose. A raw droplet IP is a smoke host, not the origin guests, Google, Stripe, or recovery mail should use.

Pin a Release tag (v0.1.5), not a drifting main. Hostname + A/AAAA first (Cloudflare: DNS only until the certificate exists). 2 vCPU / 4 GB if you build on the box. TCP 80 and 443. Docker Engine and Compose v2. Then curl — do not hand-edit .env or run Compose yourself:

curl -fsSL https://raw.githubusercontent.com/designAtHotly/hotly-os/v0.1.5/scripts/install.sh | bash -s -- --tag v0.1.5 --github designAtHotly/hotly-os

That unpacks to ~/hotly-os. Answer the prompts for https://your.domain.example (canonical origin, Open Graph, /robots.txt, /sitemap.xml). The installer writes .env, uses the prod overlay (Postgres / API / Next.js not published), and starts Compose. From a checkout of that tag: ./scripts/install.sh --local.

After that, in the provider consoles: Firebase authorized domain, Stripe webhook https://your.domain.example/api/webhooks/stripe (Checkout, subscription, and invoice events in docs/operations.md; paste whsec_ at the installer prompt), SendGrid API key starting SG. plus a verified sender (app.sendgrid.com).

Full sequence, backup/restore, upgrades, SendGrid vs Twilio keys: docs/operations.md.

License

Apache License 2.0. See LICENSE and NOTICE.

Community

About

Self-hosted Hotly for exactly one creator.

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages