- Go 91.6%
- HTML 4.1%
- Shell 3%
- CSS 1.2%
|
All checks were successful
CI / build (push) Successful in 11m39s
Bump the NodeInfo software version to 1.0.2 and summarize the changes since 1.0.1 in the changelog: the public /api/v1/communities endpoint, the public /instances page with per-rule visibility toggles, the sortable communities directory, lower delivery retry budget, pending remote joins, and the custom 404 page. |
||
|---|---|---|
| .forgejo/workflows | ||
| cmd/server | ||
| internal | ||
| scripts | ||
| .dockerignore | ||
| .gitignore | ||
| CHANGELOG.md | ||
| config.example.conf | ||
| docker-compose.yml | ||
| Dockerfile | ||
| go.mod | ||
| go.sum | ||
| LICENSE | ||
| Makefile | ||
| README.md | ||
GoThread
A lightweight, spec-compliant threadiverse (Reddit-like) server that federates over ActivityPub. One static Go binary, one SQLite file, a built-in server- rendered UI — self-host in minutes.
GoThread speaks standard ActivityStreams 2.0 types and interops with the threadiverse: it is smoke-tested round-trip against Lemmy (follow, post, comment, vote, and moderation both directions).
Features
- Full local forum — communities, posts, comments with threaded sorting (active/hot/new/top/controversial + time windows), votes, markdown-lite, search, RSS, pagination, profiles, notifications (mentions/replies/participation), private multireddit lists, NSFW communities.
- Media hosting — images and video uploads stored in the same SQLite file,
served with
Rangesupport. - Hand-rolled ActivityPub — AS2 vocab with tolerant decode, HTTP signatures (sign RSA, verify RSA + Ed25519), WebFinger, NodeInfo, inbox/outbox, fetch-by-ID, and a delivery queue with retries, backoff and idempotent dedup.
- Moderation — removes/restores, deletes, pins, locks, reports and a mod
log, personal blocks, community bans and instance allow/blocklists. Cross-
instance moderation works: bans propagate to remote instances and remote
Lemmy
ModBanFromCommunityevents are enforced locally. - Admin — rename the instance, toggle registration open/closed with invite links, a federation-health dashboard (queue depth, failures, replay), and single-file backups.
- Email (optional) — SMTP-verified accounts and password resets. With a mail server configured, new accounts must confirm their email before signing in, email changes are staged until verified, unconfirmed registrations are purged after 7 days, and forgotten passwords get a one-time reset link.
Requirements
Go 1.26+ to build. Runtime is a single static binary (no cgo, no external
services); modernc.org/sqlite is the only dependency.
Quick start
go build -o gothread ./cmd/server
./gothread
The defaults are https on 127.0.0.1:8040 with a forum_like.db next to the
binary. For local development over plain HTTP:
./gothread -scheme http -domain 127.0.0.1:8040
For a real instance, copy the example config and edit it:
cp config.example.conf my.conf
# edit my.conf (set DOMAIN, INSTANCE_NAME, SMTP_*, etc.)
./gothread -config my.conf
The config file is KEY = value lines with comments; see
config.example.conf for every key and its default.
- The first registered account becomes the instance admin.
- Register a community, then post — no further setup required.
- Check
./gothread --helpfor every flag, or hit/healthzfor a health check.
Docker
docker build -t gothread .
docker run -p 8040:8040 -e FORUM_LIKE_DOMAIN=forum.example -v gothread-data:/app/data gothread
Configuration
Precedence: defaults < config file < environment < flags. Env vars are
FORUM_LIKE_*; the config file is KEY = value lines (see --help for keys).
Notable settings:
| Flag / env | Default | Purpose |
|---|---|---|
-scheme / SCHEME |
https |
Public URL scheme (http for dev) |
-domain / DOMAIN |
host:port | Public domain, used for federation IDs |
-instance-name / INSTANCE_NAME |
GoThread |
Display name shown in the navbar and titles (seed; change later in /admin) |
-registration-mode / REGISTRATION_MODE |
open |
open or closed (invite links) — a one-time seed; later toggled in /admin/registration |
-federation-mode / FEDERATION_MODE |
blocklist |
blocklist admits all non-blocked hosts; allowlist admits only allowed hosts |
-allow-private-federation |
off | Allow federation to/from private, loopback and link-local addresses (dev/LAN) |
-require-email / REQUIRE_EMAIL |
true |
Make an email mandatory at registration. Without an SMTP server configured, registration is blocked until one is set |
-smtp-host / SMTP_HOST |
— | SMTP server for email verification and password resets (empty disables all outbound mail) |
-smtp-port / SMTP_PORT |
587 |
SMTP port |
-smtp-username / SMTP_USERNAME |
— | SMTP username (PLAIN auth, only over TLS or localhost) |
-smtp-password / SMTP_PASSWORD |
— | SMTP password |
-smtp-from / SMTP_FROM |
noreply@<domain> |
Sender address on outbound mail |
-smtp-security / SMTP_SECURITY |
starttls |
starttls (587), ssl (implicit TLS, 465), or none (plaintext, local dev only) |
-smtp-insecure / SMTP_INSECURE |
off | Skip TLS certificate verification (test servers) |
-rate-limit-enabled / RATE_LIMIT_ENABLED |
off | Per-client-IP rate limiting (set -trusted-proxies behind a proxy) |
-backup-dir / BACKUP_DIR |
backups |
Where /admin/backup writes snapshots |
-backup |
— | One-shot: write a snapshot and exit (cron-friendly) |
Federation notes
- SSRF guard: outbound federation to private/loopback/link-local targets is
refused by default. Two instances on
localhostmust both pass-allow-private-federation. - Delivery is asynchronous with retries and backoff. Watch failures and
replay dropped deliveries from the admin federation dashboard (
/admin/federation).
Email is opt-in via -smtp-host; without it, addresses are collected but never
verified. Admins can still mint one-time password reset links by hand (see below).
With a mail server configured:
- Registration — a supplied address must be confirmed from a link before the account can sign in. The username stays reserved until the link is used (or the account is purged after 7 days). If the confirmation email cannot be sent, the registration is rolled back and the username is freed.
- Login — unverified accounts get a resend page instead of a session.
- Password reset —
/forgotmails a one-time, 1-hour link; responses are deliberately identical whether or not the address exists. Resetting revokes every live session. - Admin reset links — with or without SMTP,
/admin/resetlets an admin mint a one-time, 1-hour reset link for any local account (by username or email). Issuing one supersedes any earlier reset link for that account. - Email changes — the current address stays active until the new one is
confirmed; a staged change sits as
pending_emailin settings. - A janitor runs hourly: unverified registrations older than 7 days, spent or expired tokens, and expired sessions are removed.
Backup & restore
Everything — posts, media, keys — lives in the one SQLite file.
- Backup:
/admin/backup(admin), or./gothread -backup /path/snapshot.db. - Restore: stop the server, replace the DB file with the snapshot, start again.
Development
go build ./... # build
go vet ./... # vet
go test ./... # full test suite (457+ tests, no external services)
gofmt -l . # format check, must print nothing
Smoke suites
Real servers on fixed ports (scripts/), run with go and curl:
federation_smoke.sh— two-instance federation and delivery replaymoderation_smoke.sh— blocks, thread mods, reports, audit log, announced bansmedia_smoke.sh— multipart uploads, media serve/Range/rejectionsregistration_smoke.sh— open/closed toggle, invites, optional emailinterop_smoke.sh— Lemmy round-trip (requires podman + podman-compose)
internal/httpsig/testdata/*.http and internal/ap/testdata/*.json are frozen
requests captured from the interop harness; they pin the signer/verifier to
on-wire shapes.
Layout
cmd/server/— entrypoint, wiring, graceful shutdowninternal/config/— flags/env/file configinternal/mail/— SMTP sending (net/smtponly, no new dependencies)internal/storage/— SQLite viadatabase/sql, embedded migrationsinternal/ap/— AS2/AP vocab with tolerant decodeinternal/httpsig/— HTTP signaturesinternal/federation/— delivery queue, dedup, fetch-by-ID, SSRF guardinternal/web/— routes, sessions, server-rendered templates, admin UI
License
AGPL-3.0. If you modify GoThread and serve it over a network, you must offer users the modified source (section 13).