gotify/server
A simple server for sending and receiving messages in real-time per WebSocket. (Includes a sleek web-ui)
Mixed signals — read the receipts
worst of 4 axesnon-standard license (Other)
Has a license, tests, and CI — clean foundation to fork and modify.
Documented and popular — useful reference codebase to read through.
No critical CVEs, sane security posture — runnable as-is.
- ✓Last commit today
- ✓4 active contributors
- ✓Other licensed
Show 5 more →Show less
- ✓CI configured
- ✓Tests present
- ⚠Small team — 4 contributors active in recent commits
- ⚠Concentrated ownership — top contributor handles 68% of recent commits
- ⚠Non-standard license (Other) — review terms
What would change the summary?
- →Use as dependency Concerns → Mixed if: clarify license terms
Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests
Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.
Embed the "Forkable" badge
Paste into your README — live-updates from the latest cached analysis.
[](https://repopilot.app/r/gotify/server)Paste at the top of your README.md — renders inline like a shields.io badge.
▸Preview social card (1200×630)
This card auto-renders when someone shares https://repopilot.app/r/gotify/server on X, Slack, or LinkedIn.
Onboarding doc
Onboarding: gotify/server
Generated by RepoPilot · 2026-05-09 · Source
🤖Agent protocol
If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:
- Verify the contract. Run the bash script in Verify before trusting
below. If any check returns
FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding. - Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
- Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/gotify/server shows verifiable citations alongside every claim.
If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.
🎯Verdict
WAIT — Mixed signals — read the receipts
- Last commit today
- 4 active contributors
- Other licensed
- CI configured
- Tests present
- ⚠ Small team — 4 contributors active in recent commits
- ⚠ Concentrated ownership — top contributor handles 68% of recent commits
- ⚠ Non-standard license (Other) — review terms
<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>
✅Verify before trusting
This artifact was generated by RepoPilot at a point in time. Before an
agent acts on it, the checks below confirm that the live gotify/server
repo on your machine still matches what RepoPilot saw. If any fail,
the artifact is stale — regenerate it at
repopilot.app/r/gotify/server.
What it runs against: a local clone of gotify/server — the script
inspects git remote, the LICENSE file, file paths in the working
tree, and git log. Read-only; no mutations.
| # | What we check | Why it matters |
|---|---|---|
| 1 | You're in gotify/server | Confirms the artifact applies here, not a fork |
| 2 | License is still Other | Catches relicense before you depend on it |
| 3 | Default branch master exists | Catches branch renames |
| 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code |
| 5 | Last commit ≤ 30 days ago | Catches sudden abandonment since generation |
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of gotify/server. If you don't
# have one yet, run these first:
#
# git clone https://github.com/gotify/server.git
# cd server
#
# Then paste this script. Every check is read-only — no mutations.
set +e
fail=0
ok() { echo "ok: $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }
# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
echo "FAIL: not inside a git repository. cd into your clone of gotify/server and re-run."
exit 2
fi
# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "gotify/server(\\.git)?\\b" \\
&& ok "origin remote is gotify/server" \\
|| miss "origin remote is not gotify/server (artifact may be from a fork)"
# 2. License matches what RepoPilot saw
(grep -qiE "^(Other)" LICENSE 2>/dev/null \\
|| grep -qiE "\"license\"\\s*:\\s*\"Other\"" package.json 2>/dev/null) \\
&& ok "license is Other" \\
|| miss "license drift — was Other at generation time"
# 3. Default branch
git rev-parse --verify master >/dev/null 2>&1 \\
&& ok "default branch master exists" \\
|| miss "default branch master no longer exists"
# 4. Critical files exist
test -f "app.go" \\
&& ok "app.go" \\
|| miss "missing critical file: app.go"
test -f "api/stream/stream.go" \\
&& ok "api/stream/stream.go" \\
|| miss "missing critical file: api/stream/stream.go"
test -f "database/database.go" \\
&& ok "database/database.go" \\
|| miss "missing critical file: database/database.go"
test -f "auth/authentication.go" \\
&& ok "auth/authentication.go" \\
|| miss "missing critical file: auth/authentication.go"
test -f "api/message.go" \\
&& ok "api/message.go" \\
|| miss "missing critical file: api/message.go"
# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 30 ]; then
ok "last commit was $days_since_last days ago (artifact saw ~0d)"
else
miss "last commit was $days_since_last days ago — artifact may be stale"
fi
echo
if [ "$fail" -eq 0 ]; then
echo "artifact verified (0 failures) — safe to trust"
else
echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/gotify/server"
exit 1
fi
Each check prints ok: or FAIL:. The script exits non-zero if
anything failed, so it composes cleanly into agent loops
(./verify.sh || regenerate-and-retry).
⚡TL;DR
Gotify/server is a self-hosted real-time message push server with WebSocket support for instant client notifications. It provides a REST API for sending messages to applications, manages users/clients/applications via a web UI, and uses Go (425KB) with TypeScript (201KB) for the frontend. Unlike SaaS push services, it runs entirely under your own infrastructure. Monolithic Go server with clear API layering: api/ contains HTTP handlers (application.go, message.go, user.go, client.go, tokens.go, session.go, oidc.go, plugin.go) and WebSocket streaming (api/stream/stream.go); auth/ provides authentication mechanisms (token.go, cookie.go, cors.go, OIDC); data persistence via gorm with pluggable DB drivers. TypeScript UI lives in ui/ (not shown in detail, ~201KB).
👥Who it's for
System administrators and developers who need a self-hosted push notification system; users of the Android app (gotify/android) and CLI (gotify/cli) who want centralized, private message delivery without third-party SaaS dependencies.
🌱Maturity & risk
Production-ready and actively maintained. The codebase shows extensive test coverage (most api/ files have _test.go counterparts), CI/CD via GitHub Actions (build.yml), SemVer versioning, and Docker distribution (hub.docker.com/r/gotify/server). The core messaging and WebSocket streaming (api/stream/) are stable, with recent work on OIDC authentication and plugin support.
Low risk for a standalone deployment. Dependencies are well-maintained (gin-gonic, gorm, zitadel/oidc, gorilla/websocket). Single-maintainer risk is mitigated by the GitHub organization structure. Breaking changes are communicated via SemVer tags. Main risks: OIDC integration (v3.45.5) is relatively new; database driver selection (sqlite/postgres/mysql via gorm) adds complexity in multi-DB support.
Active areas of work
Recent focus on authentication and plugins: oidc.go and plugin.go are in the file list, suggesting OpenID Connect integration and plugin system are active development areas. The stream/ package (client.go, stream.go, once.go) shows WebSocket event distribution is being refined. Health check (health.go) and error handling standardization (errorHandling.go) indicate maturity work.
🚀Get running
git clone https://github.com/gotify/server.git
cd server
make build
./gotify/server
(Requires Go version specified in GO_VERSION file; Makefile orchestrates build. See CONTRIBUTING.md for detailed dev setup.)
Daily commands:
make build
./gotify/server
Server starts on configured port (default 80); requires environment setup per config docs at gotify.net/docs/config. For dev: Makefile targets include test, lint (golangci.yml configured).
🗺️Map of the codebase
app.go— Root application entry point that orchestrates the server initialization, router setup, and all middleware configuration.api/stream/stream.go— Core WebSocket streaming abstraction for real-time message delivery—the foundational pattern for client connections.database/database.go— Database layer abstraction using GORM; all persistence operations depend on this initialization and schema.auth/authentication.go— Authentication middleware and strategy dispatch; controls access to all protected API endpoints and WebSocket streams.api/message.go— Message API handler; implements the core business logic for creating, retrieving, and broadcasting messages across clients.config/config.go— Configuration loading and validation; sets up database drivers, OIDC, storage paths, and all runtime parameters.model/message.go— Core message model definitions; schema contract shared across database, API, and WebSocket layers.
🛠️How to make changes
Add a new REST API endpoint
- Create a new handler function in api/ (e.g., api/myfeature.go) that receives *gin.Context (
api/myfeature.go) - Define the request/response models in model/ if needed (e.g., model/myfeature.go) (
model/myfeature.go) - Register the route in app.go by calling router.POST/GET/PUT/DELETE with your handler (
app.go) - Add database methods in database/myfeature.go if persistence is required (
database/myfeature.go) - Write tests mirroring api/myfeature_test.go patterns using test fixtures and mocks (
api/myfeature_test.go)
Add a new WebSocket message type
- Add the message type definition to model/message.go or create model/mynewmsg.go (
model/message.go) - Update database/message.go to handle persistence and filtering for the new type (
database/message.go) - Modify api/stream/stream.go to broadcast the new message type to subscribed clients (
api/stream/stream.go) - Add JSON serialization tags and validation in the model; update swagger annotations in docs/spec.json (
model/message.go) - Write integration tests in api/message_test.go to verify WebSocket delivery (
api/message_test.go)
Add a new authentication method
- Create a new auth strategy file (e.g., auth/myauth.go) implementing the authentication logic (
auth/myauth.go) - Update auth/authentication.go to register your strategy in the middleware dispatch (
auth/authentication.go) - Add configuration fields to config/config.go for your auth method (e.g., MyAuthConfig struct) (
config/config.go) - Create corresponding API endpoints in api/session.go or similar to manage your auth method (
api/session.go) - Write tests in auth/myauth_test.go and api/session_test.go to verify token generation and validation (
auth/myauth_test.go)
Add a new database model and CRUD operations
- Define the model struct in model/mynewmodel.go with GORM tags and JSON serialization (
model/mynewmodel.go) - Create database/mynewmodel.go with CRUD methods; GORM will auto-migrate the schema (
database/mynewmodel.go) - Create api/mynewmodel.go with HTTP handlers (GET/POST/PUT/DELETE) that call database layer (
api/mynewmodel.go) - Register routes in app.go with appropriate auth middleware guards (
app.go) - Write tests in database/mynewmodel_test.go for persistence and api/mynewmodel_test.go for HTTP behavior (
database/mynewmodel_test.go)
🔧Why these technologies
- Go + Gin Web Framework — Lightweight, fast HTTP routing with middleware support; native concurrency for handling many WebSocket connections simultaneously.
- WebSocket (gorilla/websocket) — Enables full-duplex real-time message delivery to connected clients; allows server to push messages without polling.
- GORM ORM — Database-agnostic abstraction supporting SQLite (dev), PostgreSQL, and MySQL (prod); automatic schema migration.
- undefined — undefined
🪤Traps & gotchas
- Database initialization: gorm auto-migration runs on startup; no explicit schema setup script needed, but database connection string must be valid (sqlite/postgres/mysql syntax differs). 2) WebSocket URL routing: stream clients connect to a distinct WebSocket endpoint (not REST); verify auth middleware is applied to ws://(host)/stream (see session.go for routing). 3) Plugin paths: plugins are loaded from a specific directory; GOTIFY_PLUGIN_PATH env var must be set. 4) CORS and Origin checks: auth/cors.go may reject cross-origin WebSocket upgrades if not configured; test with explicit allowed origins. 5) Docker build: Dockerfile uses multi-stage build; ensure GO_VERSION file is readable during build.
🏗️Architecture
💡Concepts to learn
- WebSocket pub-sub broker — Gotify's core is a multi-client message fan-out system (api/stream/stream.go); understanding how clients subscribe to application topics and receive broadcasts in real-time is essential to modifying message delivery
- OpenID Connect (OIDC) — api/oidc.go integrates zitadel/oidc for enterprise SSO; new contributors adding auth features must understand OIDC flows (authorization code, discovery, JWT validation)
- Middleware-based authentication — auth/ directory implements pluggable auth strategies (token, cookie, CORS) applied via gin middleware; understanding the auth stack is required to add new auth methods or secure new endpoints
- ORM-based database abstraction — gorm (go.mod line 18) abstracts sqlite/postgres/mysql; new schema changes must use gorm structs (Tag: gorm:"...") and auto-migration, not raw SQL
- Plugin API contracts — api/plugin.go loads external Go plugins via gotify/plugin-api; understanding the plugin interface is needed to extend Gotify without forking (e.g., custom message processors, integrations)
- Session lifecycle and WebSocket upgrade — api/session.go manages HTTP → WebSocket upgrade and client state (auth, subscriptions); this is the gateway between REST clients and streaming subscribers
- Cron-based task scheduling — robfig/cron (go.mod) handles periodic cleanup or delivery retries; understanding scheduled tasks is needed if adding time-based features (e.g., message expiry, batch sends)
🔗Related repos
gotify/cli— Official CLI for sending messages to a Gotify server; ships alongside gotify/server in user deploymentsgotify/android— Official Android app for receiving push notifications from a Gotify server; primary mobile client for the systemgotify/plugin-api— Plugin interface library (github.com/gotify/plugin-api v1.0.0 in go.mod); defines contract for extending server via plugins/ntfy/ntfy— Alternative self-hosted push notification system with simpler HTTP-only design; competitor in the same problem spacemattn/pushbullet-go— PushBullet Go SDK; represents the third-party SaaS alternative that Gotify replaces with self-hosted infrastructure
🪄PR ideas
To work on one of these in Claude Code or Cursor, paste:
Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.
Add comprehensive integration tests for WebSocket stream functionality
The api/stream package (stream.go, client.go, once.go) handles real-time message delivery via WebSocket - a core feature of Gotify. While unit tests exist (*_test.go files), there are no integration tests that verify the complete flow: client connection → authentication → message streaming → disconnection. This is critical for a real-time messaging server.
- [ ] Create api/stream/integration_test.go with test cases for: successful WebSocket connection with valid token, message delivery to connected clients, client disconnection handling, and concurrent multi-client scenarios
- [ ] Reference existing auth/authentication_test.go patterns for token validation in tests
- [ ] Use gorilla/websocket (already in dependencies) to simulate client connections
- [ ] Verify message ordering and delivery guarantees across multiple concurrent streams
Add database migration validation and rollback tests
The database/migration_test.go file exists but is likely incomplete. Gotify supports MySQL, PostgreSQL, and SQLite (gorm drivers in dependencies). Database migrations are critical infrastructure - they must be tested across all supported databases to prevent data loss or corruption during upgrades.
- [ ] Expand database/migration_test.go to test migrations for all three database types (MySQL, PostgreSQL, SQLite) in CI
- [ ] Add test cases for: forward migration (upgrade), rollback scenarios, schema consistency post-migration, and concurrent migration safety
- [ ] Ensure tests verify that all tables (application, client, message, plugin tables from database/*.go files) are created correctly
- [ ] Create separate test database fixtures for each database type in test utilities
Add OIDC/CORS security tests with attack scenarios
The auth/oidc.go and auth/cors.go modules handle authentication and cross-origin requests - security-critical components. While unit tests exist (oidc_test.go, cors_test.go), they likely lack tests for common attack scenarios like CSRF, token replay, origin spoofing, and OIDC state parameter attacks.
- [ ] Expand auth/cors_test.go to include tests for: missing origin header, invalid origin, origin spoofing attempts, and credential-with-wildcard rejection
- [ ] Expand auth/oidc_test.go to test: OIDC state parameter validation, token expiration handling, malformed JWT tokens, and redirect URI validation against registered URLs
- [ ] Reference OWASP guidelines and add specific test cases for OAuth 2.0/OIDC security best practices
- [ ] Verify that auth/util_test.go covers edge cases in token/session utilities used by both modules
🌿Good first issues
- Add integration tests for api/plugin.go (file exists but no _test.go); verify plugin lifecycle (load, unload, error handling) with mock plugin-api
- Write missing tests for api/health.go; currently no health_test.go but health checks are critical for k8s deployments—add cases for DB availability, memory, uptime
- Document stream/ package architecture: api/stream/ has no README; new contributors don't understand how stream.go's broadcast loop, client.go registration, and once.go single-delivery interact—add inline docs and a STREAM.md guide
⭐Top contributors
Click to expand
Top contributors
- @jmattheis — 68 commits
- @renovate[bot] — 25 commits
- @eternal-flame-AD — 5 commits
- @AlliotTech — 2 commits
📝Recent commits
Click to expand
Recent commits
ec8ce07— Merge pull request #956 from gotify/version (jmattheis)d02530c— Revert "ci: checkout full history" (jmattheis)18a300d— fix: master build version (jmattheis)9f54b1e— ci: checkout full history (jmattheis)fd3c5ac— Merge pull request #953 from gotify/build-master-docker (jmattheis)0e39fa1— ci: build master docker image (jmattheis)9e5a6b5— Merge pull request #952 from gotify/oidc-next (jmattheis)ef36e75— fix: /client:elevate to /client/:id/elevate (jmattheis)0ac515f— fix: minutes granularity for client elevation (jmattheis)a5fc2bc— fix: unset elevated until if in the past (jmattheis)
🔒Security observations
- High · Outdated Go Crypto Library —
go.mod - golang.org/x/crypto v0.48.0. The codebase uses golang.org/x/crypto v0.48.0, which may contain known vulnerabilities. Go crypto library updates frequently patch security issues. Fix: Update to the latest version of golang.org/x/crypto. Run 'go get -u golang.org/x/crypto' to fetch the latest patches. - Medium · WebSocket Library Security —
go.mod - github.com/gorilla/websocket v1.5.3, api/stream/stream.go. The codebase uses gorilla/websocket v1.5.3 for real-time communication. While relatively recent, WebSocket implementations are common attack vectors for XSS and injection attacks if message validation is insufficient. Fix: Ensure all WebSocket messages are properly validated and sanitized before processing. Implement rate limiting on WebSocket connections to prevent DoS attacks. - Medium · OIDC Implementation Security —
api/oidc.go, api/oidc_test.go. The codebase integrates with OIDC (api/oidc.go) using zitadel/oidc/v3. OIDC implementations require careful handling of tokens, redirects, and state parameters to prevent authorization bypass attacks. Fix: Verify that OIDC state parameter validation is implemented, redirect URI whitelisting is enforced, and token validation is complete. Review for open redirect vulnerabilities. - Medium · CORS Configuration Potential Risk —
auth/cors.go, auth/cors_test.go. The codebase includes CORS handling (auth/cors.go) using gin-contrib/cors. Improper CORS configuration could allow unauthorized cross-origin requests. Fix: Verify that AllowOrigins is not set to '*' in production. Use explicit whitelist of allowed origins. Review CORS configuration in config.example.yml and runtime config. - Medium · SQL Injection Risk in Database Layer —
database/ directory (database.go, application.go, client.go, message.go, user.go, plugin.go). The codebase uses GORM for database operations (gorm.io/gorm v1.31.1). While GORM provides parameterized queries by default, raw SQL queries could introduce SQL injection if user input is not properly escaped. Fix: Audit all database queries to ensure parameterized queries are used exclusively. Avoid string concatenation with user inputs. Use GORM's query builder methods. - Medium · Plugin System Security —
api/plugin.go, api/plugin_test.go, database/plugin.go. The codebase implements a plugin system (api/plugin.go, database/plugin.go) using gotify/plugin-api. Dynamic plugin loading can be a significant security risk if plugin validation and sandboxing are insufficient. Fix: Implement cryptographic verification of plugins before loading. Restrict plugin permissions to the minimum necessary. Consider running plugins in isolated containers or processes. - Medium · Authentication Token Handling —
auth/token.go, auth/token_test.go, api/tokens.go, api/tokens_test.go. Token-based authentication (auth/token.go, api/tokens.go) is implemented. Improper token storage, transmission, or validation could lead to authentication bypass. Fix: Ensure tokens are transmitted over HTTPS only. Implement token expiration and refresh mechanisms. Use secure, httpOnly cookies where applicable. Validate tokens on every protected request. - Low · Session Management —
auth/cookie.go, api/session.go, api/session_test.go. Session handling is implemented (auth/cookie.go, api/session.go). Session fixation or hijacking could occur if session tokens are not properly managed. Fix: Implement secure session ID generation using cryptographically strong random sources. Use httpOnly and secure flags on cookies. Implement session timeout and regeneration on authentication. - Low · Error Handling Information Disclosure —
undefined. Custom error handling is implemented (error/handler.go). Error messages could potentially leak sensitive information about the application structure Fix: undefined
LLM-derived; treat as a starting point, not a security audit.
👉Where to read next
- Open issues — current backlog
- Recent PRs — what's actively shipping
- Source on GitHub
Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.