Skip to content

ShuttleLab/smsforwarder-api

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

smsforwarder-api

Multi-user backend for the SMS Forwarder app

Receives SMS uploaded by the phone client, authenticates the user, and fans each message out to that user's configured channels (Telegram). A single Go binary with an embedded SQLite store and a built-in operator console — no external services.

📱 Client: SMSForwarder  ·  🌐 SMS Forwarder — forward SMS to Telegram

Go SQLite Telegram License

About

smsforwarder-api is the server half of SMS Forwarder. The phone uploads each incoming SMS to POST /api/sms; the server authenticates the device, stores the message briefly, and delivers it to the user's channels. Channel configuration lives here (not in the app), so channels can be added or changed without rebuilding the client.

It is relay-and-forget: an SMS body lives in the outbox only until it is delivered (or finally given up), then it is deleted. Only metadata is kept in the delivery log, visible to the operator. Modeled on the sibling secretary project — one binary, pure-Go SQLite (no CGO), no message queue, no ORM.

The official instance is operated by ShuttleLab at smsforwarder-api.shuttlelab.org, and the released app talks to it out of the box. Because the code is open source (AGPL-3.0), anyone can also run their own instance.

Key Features

  • Multi-user — open registration; every user's data is isolated (all queries scoped by user_id). A message can never be delivered to the wrong user.
  • Device-token auth — sign in once with username + password; the server issues a long-lived, per-device token (stored only as a SHA-256 hash) that the app sends as a Bearer credential. Tokens are individually revocable.
  • Telegram channel — one shared bot handles every user. Users bind by tapping a deep link and pressing Start (/bind flow); the bot writes their chat_id straight into the store. No tokens to copy.
  • Durable delivery — an outbox with an exponential-backoff retry engine survives restarts; long messages are auto-split under Telegram's 4096-char limit; delivery is idempotent via a per-message key.
  • Self-healing bindings — when Telegram reports a recipient is unreachable (403: bot blocked / can't initiate), the binding is cleared automatically so the app prompts the user to re-bind. There is also a one-click "reset all Telegram bindings" for when the shared bot is replaced.
  • Operator admin console — an embedded web UI (session-cookie login) for delivery logs, user management (ban / revoke devices / delete), per-table data maintenance, and bot settings. Binds to localhost by default.
  • No cloud dependencies — no GMS/FCM, no external DB. A single binary plus one SQLite file.

Tech Stack

  • Language: Go 1.25
  • Storage: modernc.org/sqlite (pure Go, no CGO), WAL mode
  • Telegram: github.com/go-telegram/bot (long polling)
  • HTTP / admin UI: standard library net/http + html/template
  • Passwords: bcrypt (golang.org/x/crypto)

Getting Started

Prerequisites

  • Go 1.25+

Run

cp .env.example .env      # optional; sane defaults otherwise
go run .                  # API on HTTP_ADDR (default 0.0.0.0:8090)

Configuration (env, see .env.example): DB_PATH, HTTP_ADDR, RATE_LIMIT_PER_MIN, BOT_TOKEN, BOT_USERNAME, ADMIN_ADDR, ADMIN_USER, ADMIN_PASSWORD. The Telegram bot token/username may also be set in the admin console (DB overrides env; a changed token needs a restart).

API

Method Path Auth Notes
GET /healthz liveness
POST /api/register {username, password}
POST /api/login {username, password, device_label?}{device_token}
POST /api/logout Bearer revoke current device
GET /api/channels Bearer list
POST /api/channels Bearer {type, config, enabled?}
GET/PATCH/DELETE /api/channels/{id} Bearer read / edit / delete
POST /api/channels/{id}/test Bearer send a test message now
POST /api/channels/{id}/bind-code Bearer Telegram bind code + deep link
POST /api/sms Bearer {sender, content, timestamp(ms), client_msg_id}

Send Authorization: Bearer <device_token> (returned once at login). Channel config per type: telegram{"chat_id":<int>} (filled by /bind).

Admin console

Enabled when ADMIN_USER + ADMIN_PASSWORD are set; served on ADMIN_ADDR (default 127.0.0.1:8091). Keep it on localhost and reach it over an SSH tunnel:

ssh -L 8091:127.0.0.1:8091 <server>   # then open http://127.0.0.1:8091/admin

Deploy

Single binary + SQLite, same host as secretary. Expose it with a Cloudflare Tunnel (no public inbound port): add an ingress rule mapping smsforwarder-api.shuttlelab.org to http://127.0.0.1:8090, then run behind systemd.

go build -o smsforwarder-api .
# HTTP_ADDR=127.0.0.1:8090 (localhost only; the tunnel reaches it)
sudo systemctl restart smsforwarder-api
curl -s https://smsforwarder-api.shuttlelab.org/healthz   # {"ok":true}

License

Licensed under the GNU Affero General Public License v3.0 (AGPL-3.0) — see LICENSE. You may use, study, modify and redistribute it, but any distributed or network-served derivative (including a hosted instance) must also be released under AGPL-3.0.

Built by ShuttleLab

About

Backend for SMS Forwarder — receives SMS from the Android client and relays each to Telegram. Single self-hostable Go binary with embedded SQLite and a built-in console.

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Used by

Contributors

Languages