Files
server-scripts/haven-notify
Jose Henrique b1cdf15cc8
Some checks failed
Check scripts syntax / check-scripts-syntax (push) Successful in 3s
Test automated-nfs-backup / test-automated-nfs-backup (push) Successful in 4s
Haven Notify Build and Deploy / Build Haven Notify Image (pull_request) Has been cancelled
Haven Notify Build and Deploy / Build Haven Notify Image (PR) (pull_request) Has been cancelled
Haven Notify Build and Deploy / Deploy Haven Notify (internal) (pull_request) Has been cancelled
Haven Notify Build and Deploy / Test Haven Notify (pull_request) Has been cancelled
adding test containers
2026-07-16 09:14:51 -03:00
..
2025-10-06 22:14:42 -03:00
2026-07-16 09:14:51 -03:00
2026-07-16 09:14:51 -03:00
2026-07-16 09:14:51 -03:00
2026-07-16 09:14:51 -03:00
2026-07-16 09:14:51 -03:00
2026-07-16 09:14:51 -03:00
2026-07-16 09:14:51 -03:00
2026-07-16 09:14:51 -03:00
2026-07-16 09:14:51 -03:00
2026-07-16 09:14:51 -03:00
2026-07-16 09:14:51 -03:00
2026-07-16 09:14:51 -03:00
2026-07-16 09:14:51 -03:00
2026-07-16 09:14:51 -03:00
2026-07-16 09:14:51 -03:00

Haven Notify Logo

Overview

Haven Notify is an internal Go service that validates notification requests, renders Discord-compatible payloads, and delivers them to a configured Discord webhook.

It runs as a standalone binary or container. Templates are embedded in the binary and validated at startup.

Prerequisites

  • Go 1.25.10
  • A Docker API compatible with Testcontainers for the end-to-end suite
  • A Discord webhook URL for normal service operation

WEBHOOK_URL is required and must be an absolute HTTP or HTTPS URL. The process exits before becoming ready when the configuration or embedded templates are invalid.

API

All notification endpoints require POST and Content-Type: application/json. Request bodies are limited to 64 KiB, documented fields are required, and unknown fields are accepted for forward compatibility.

Send Notification

  • Endpoint: /notify
  • Request Body:
{
  "title": "Notification Title",
  "message": "Notification Message"
}

Send Backup Notification

  • Endpoint: /template/notify/backup
  • Request Body:
{
  "title": "Nightly backup",
  "asset": "Photos",
  "backupSizeInMB": 1536,
  "extra": [
    {
      "name": "Host",
      "value": "nebula"
    }
  ]
}

Send Update Notification

  • Endpoint: /template/notify/update
  • Request Body:
{
  "host": "nexus",
  "asset": "k3s",
  "time": 42
}

time is expressed in seconds.

Send Error Notification

  • Endpoint: /template/notify/error
  • Request Body:
{
  "caller": "backup-job",
  "message": "Error while moving file",
  "critical": true,
  "extra": [
    {
      "name": "Path",
      "value": "/mnt/backups"
    }
  ]
}

Health Probes

  • GET or HEAD /ready returns 200 Ready after configuration and templates initialize.
  • GET or HEAD /live returns 200 Alive while the process can serve HTTP.

Response Statuses

Status Meaning
200 Notification delivered
400 Invalid or missing request data
404 Unknown route or template
405 Unsupported method
415 Content type is not JSON
502 Discord rejected the request or delivery retries failed
504 Discord delivery exceeded its timeout

Haven Notify suppresses Discord mention parsing and safely truncates content to Discord's message and embed limits. Transport errors, rate limits, and server errors receive at most two retries within a ten-second total delivery budget.

Run

Go

WEBHOOK_URL=https://discord.com/api/webhooks/... go run .

Docker

docker build -t haven-notify .
docker run --rm -p 8080:8080 \
  -e WEBHOOK_URL=https://discord.com/api/webhooks/... \
  haven-notify

The runtime container uses a non-root haven-notify user and contains only the binary, Alpine runtime files, and CA certificates.

Tests

Run unit and HTTP contract tests:

go test ./...

Run static analysis, the race detector, and coverage (the race detector requires CGO support):

go vet ./...
go test -race -shuffle=on -count=1 -covermode=atomic -coverprofile=coverage.out ./...
go tool cover -func=coverage.out

Run the seeded fuzz target:

go test -run=^$ -fuzz=FuzzErrorNotificationHandler -fuzztime=10s .

Run the black-box container suite:

go test -tags=e2e -run '^TestContainerE2E$' -count=1 -timeout=5m -v .

The end-to-end suite builds the production Dockerfile, starts Haven Notify and a disposable MockServer on an isolated network, and verifies the complete HTTP-to-Discord payload flow. It never calls a real Discord webhook. Configure DOCKER_HOST or the Testcontainers host settings when the Docker daemon is not available through the platform's default socket.

Deployment

The Kubernetes Deployment, Service, and Ingress manifest lives in the haven homelab repository at infra/haven-notify.yaml. This repository holds the service source, Dockerfile, tests, and Gitea Actions pipeline.

The production manifest maps the HAVEN_WEBHOOK_URL key from the discord-webhook secret into the container's WEBHOOK_URL environment variable.