Commit Graph

43 Commits

Author SHA1 Message Date
igodwin b4b48067cc ci: make lint and vuln jobs advisory until pre-existing backlog clears
CI / Lint (push) Failing after 24s
CI / Vulnerability scan (push) Failing after 1s
CI / Test (push) Failing after 24s
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 09:17:35 -07:00
igodwin 315027ab0d ci: add Gitea Actions pipeline, golangci-lint config, vuln target
- .gitea/workflows/ci.yml: lint, race tests (e2e excluded), and
  govulncheck on push to main and PRs; shared composite action installs
  protoc + pinned protoc-gen-go/protoc-gen-go-grpc and generates the
  (gitignored) protobuf code before each Go job.
- .golangci.yml (v2 schema): govet, staticcheck, errcheck, ineffassign,
  unused, misspell, gosec, revive; generated api/grpc/pb excluded.
- Makefile: vuln target (govulncheck) added and chained into qa.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 09:16:12 -07:00
igodwin 72f154ab07 feat(observability): slog-backed logging, Prometheus metrics, grpc health
- internal/logging now wraps log/slog; logging.format json/text finally
  works (json is the documented default). Same exported API.
- New internal/metrics: /metrics on the configured metrics port with
  notification gauges by status/type, queue depth, and HTTP request
  count/duration labeled by mux route pattern; sampled from service
  stats so the service layer stays metrics-agnostic.
- Standard grpc.health.v1 health service registered (k8s gRPC probes);
  gRPC MaxRecvMsgSize bounded to match the REST 1 MB body limit.
- Dedicated health listener on health_check.port serving /health and
  /readyz (probes now work in grpc-only mode); metrics, health, and
  REST servers all shut down gracefully.
- main wires retry backoff, CORS, readiness checks, and TLS from config.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 09:16:12 -07:00
igodwin ee82522b7c feat(rest,config): wire CORS, real readiness, TLS options, error hygiene
- CORS config is now actually applied to the router (the middleware
  existed but was never wired); preflight returns 204 for allowed
  origins and 403 with no CORS headers for disallowed ones.
- /readyz runs real dependency checks (queue, auth database) and
  returns 503 with per-component detail when not ready; exported
  handlers support dedicated health listeners.
- Optional server.tls (cert_file/key_file) for REST and gRPC, validated
  at config load.
- 5xx responses no longer echo internal error details; not-found and
  already-sent map to 404/409 on cancel/retry.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 09:15:57 -07:00
igodwin 21990f2533 fix(service,queue): eliminate data races, add retry backoff, tenant scoping
- Copy discipline for notifications: the store, the queue, workers, and
  API callers each own clones; no notification object is shared across
  goroutines (races previously flagged by -race between workers mutating
  Status/RetryCount and handlers JSON-encoding the same pointer).
- Retry progress derives from QueueMessage.Attempt so it survives
  requeues; exponential backoff (1s base, 30s cap) honors the documented
  queue.retry_backoff setting instead of hammering failing providers in a
  tight loop; shutdown abandons pending backoff waits cleanly.
- Stop() cancels a service-lifetime context so idle workers blocked in
  Dequeue exit immediately instead of waiting out their poll timeout.
- LocalQueue no longer holds its mutex while sending on the queue
  channel (Enqueue/Nack) — with a full buffer this deadlocked the entire
  worker pool, since draining requires the same mutex.
- Tenant scoping: notifications are stamped with the caller's ClientID;
  non-admin clients can only read/cancel/retry their own (cross-tenant
  access reports not-found to avoid leaking existence).
- Sentinel errors ErrNotificationNotFound/ErrNotificationAlreadySent.
- New race, backoff, and tenant-scoping test suites.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 09:15:57 -07:00
igodwin 90287d5da0 chore(deps): add prometheus client; bump grpc and x/net past known CVEs
govulncheck flagged GO-2026-5026/GO-2026-4918 (x/net) and GO-2026-4762
(grpc); both now at patched versions.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 09:15:57 -07:00
igodwin e332403222 fix(smtp): prevent header injection and honor use_tls
- Validate all recipients with net/mail.ParseAddress; reject CR/LF.
- RFC 2047 (Q-encoding) for Subject and FromName so CRLF and non-ASCII
  cannot break out of headers.
- Honor use_tls: implicit TLS on port 465 with certificate verification;
  otherwise document the opportunistic-STARTTLS path.
- Table-driven tests for validation, injection neutralization, and
  multipart building.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 20:27:49 -07:00
igodwin 172c240d1b fix(auth): repair persistence layer and stop storing plaintext keys
- Store SHA-256 digests (key_hash + key_preview) instead of raw keys, in
  both the in-memory store and Postgres; migrate legacy plaintext rows in
  place and drop the plaintext column.
- Fix TEXT[] scans that failed at runtime (missing pq.Array) in
  GetKey/ListKeys/LoadAllKeys.
- Load persisted keys at startup (InitializeFromDatabase was never called)
  and fall back to the database on cache miss, so issued keys survive
  restarts.
- Make HybridKeyStore.CreateKey genuinely write-through: cache is only
  updated after a successful DB write.
- Guard nil database backend (auth enabled without DB previously panicked
  on key creation) and degrade to in-memory operation.
- Persist bootstrap admin keys when a database is configured.
- Record real audit-log details as JSON and log audit failures instead of
  silently dropping them; add DB pool limits and ping timeout.
- Sentinel errors matched with errors.Is; unit tests for hashing,
  write-through ordering, DB fallback, and nil-DB operation.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 20:27:49 -07:00
igodwin d38c700949 docs(notifier): describe generic GitOps deployment; guard private plan doc
Replace the k8s/-centric deployment section with a provider-agnostic
GitOps pattern (Kustomize base+overlay, pinned tags, operator-managed
secrets, Gateway API routing) and demote k8s/ to reference examples.
Gitignore docs/WEBUI_PLAN.md: this repo is public and that doc holds
private infrastructure details (now relocated to the private gitops repo).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 20:08:17 -07:00
igodwin 3e84b9c3ad build(notifier): support multi-arch image builds
Build on the native host arch (--platform=$BUILDPLATFORM) and cross-compile
the static binary per target via buildx-provided TARGETOS/TARGETARCH, so
`make docker-build` with REGISTRY set produces linux/amd64 + linux/arm64
images without emulating the Go toolchain under QEMU. Also correct the CMD
comment to the real env var (NOTIFIER_SERVER_MODE).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
v0.1.4
2026-06-29 14:19:56 -07:00
igodwin f060c09668 docs(notifier): refresh API reference and fix env/registry examples
Document the previously-missing endpoints (GET /api/v1/notifiers and the
/api/v1/admin/keys management routes), mark the gRPC API and API-key auth
as implemented, and add an Authentication section plus links to the docs
guides. Fix incorrect examples: the env prefix is NOTIFIER_ (so
NOTIFIER_SERVER_MODE, not SERVER_MODE) and the multi-arch build var is
REGISTRY (not DOCKER_REGISTRY).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 14:19:56 -07:00
igodwin fa813c011c build: route docker-build through buildx for multi-arch + push
Setting REGISTRY now switches `make docker-build` from a single-arch
local build to a multi-arch (linux/amd64+linux/arm64) buildx build
that pushes $REGISTRY/$IMAGE:$VERSION and :latest. The redundant
docker-buildx target is removed. Bump the builder base image to
golang:1.25-alpine so protoc-gen-go-grpc@latest installs cleanly.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-12 00:07:59 -07:00
igodwin ac3ba35736 Add html_body field for multipart email notifications
Callers can now supply a plain-text Body alongside an HTML html_body;
the SMTP sender emits multipart/alternative using both verbatim instead
of auto-stripping HTML to derive the plain-text fallback. The legacy
content_type=HTML path is preserved (deprecated) for existing callers.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-06 23:59:12 -07:00
igodwin a35b3e6283 Fix nil pointer panic when notifier Send returns nil result
Guard against nil result before accessing result.Error in processNotification,
and add NtfyNotifier.Validate override so DefaultTopic is considered before
rejecting notifications with zero recipients.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-05 01:06:27 -07:00
egodwin c04db89633 Add multi-architecture Docker build support via buildx
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-28 15:27:48 -07:00
igodwin 298c960808 Fix 8 high-severity audit findings across security, Go, API, and container domains
- Use typed context key for auth context to prevent collisions (auth.go)
- Eliminate nested locking in CheckRateLimit to prevent potential deadlock (auth.go)
- Add 1MB request body size limit middleware to prevent DoS (router.go)
- Return proper gRPC status codes instead of nil errors on failures (handler.go)
- Use key name instead of raw API key in admin URL paths to prevent secret leakage (keys.go, router.go, keystore_db.go, keystore_hybrid.go)
- Enforce RBAC authorization in service Send/SendBatch for both REST and gRPC (service.go)
- Pin runtime Docker image to alpine:3.21 for reproducible builds (Dockerfile)
- Enable readOnlyRootFilesystem with /tmp emptyDir in k8s deployment (deployment.yaml)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-26 20:17:51 -07:00
igodwin 71b02758d7 Fix HTML content type handling with case-insensitive validation
- Normalize content_type to lowercase before processing to handle case-insensitive input (e.g., "HTML", "Html")
- Add validation in Validate() to ensure only valid content types are accepted
- Return descriptive error message if invalid content type is provided
- Fix condition in ToNotification() to properly detect and default content type
- Update SMTP notifier auto-detection to work correctly when content type is not explicitly set

Issues fixed:
1. Dynamically detecting content type now works correctly (was always defaulting to "text")
2. Client can now specify content type in request as "HTML", "html", or "Html" - all work
3. Invalid content types are rejected with clear error messages
4. Auto-detection still works if neither explicit type nor valid HTML markers are found

Example scenarios:
- No content_type field: auto-detects based on body (checks for <, <html, <!DOCTYPE, <p>, <div>, <br>)
- content_type: "html": sends as HTML with multipart/alternative
- content_type: "HTML": normalized to "html", sends as HTML
- content_type: "invalid": returns validation error
- content_type: "text": explicitly sends as plain text, skips auto-detection

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-31 01:49:38 -07:00
igodwin 4e594d3a7d Fix RBAC authorization logic to only return notifiers with matching roles
- Change IsAuthorized() to use deny-by-default when RBAC is enabled
- If ANY authorization rules are configured, only notifiers with explicit allowed_roles are accessible
- Notifiers without rules are denied access when RBAC is active
- If NO rules are configured, maintain open access for backward compatibility
- Add HasRules() helper method to check if RBAC is enabled

This fixes the issue where notifiers WITHOUT allowed_roles were being returned instead of
the notifiers WITH matching allowed_roles. Now when RBAC is configured:
- Only notifiers with explicit rules that match the user's roles are returned
- All other notifiers are hidden from the client

Example: If only email has allowed_roles=['admin'] and user has role 'admin':
- OLD: email ✓, stdout ✓ (WRONG - stdout should be hidden)
- NEW: email ✓, stdout ✗ (CORRECT - only email is returned)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-31 01:43:18 -07:00
igodwin 5eaf6fe6fb Redact database URL passwords from logs
- Add SanitizeDatabaseURL() function to config package that redacts passwords from database connection URLs
- Handles various URL formats: postgresql, mysql, etc.
- Correctly handles passwords containing special characters including @ symbols by using LastIndex
- Update startup logging in cmd/server/main.go to use sanitized database URL
- Add comprehensive tests covering various URL formats and edge cases

This ensures sensitive database credentials are not exposed in application logs.
2025-10-31 00:40:33 -07:00
igodwin 1cbe58888c Implement production and development build targets with binary size optimization
- Add LDFLAGS_BASE for version info only, LDFLAGS for production (with -s -w optimization), and LDFLAGS_DEV for development
- Create 'build' target (production) that strips symbols, reducing binary size by ~30% (56MB -> 39MB)
- Create 'build-dev' target (development) that keeps debug symbols for profiling
- Update 'docker-build' to pass BUILD_FLAGS="-s -w" for optimized production images
- Create 'docker-build-dev' target that builds development images with notifier:latest-dev tag and no optimization
- Update Dockerfile to accept and use BUILD_FLAGS argument in build stage
- All targets now clearly indicate their optimization level in output messages
2025-10-31 00:06:42 -07:00
igodwin 56bfcb59d3 Fix missing imports and restore CORS middleware for tests
After conflict resolution from rebase, some imports were accidentally
removed and the CORS middleware function was eliminated but still
referenced by tests. This commit:
- Adds fmt import to api/rest/keys.go (used for error messages)
- Adds gorilla/mux import to cmd/server/main.go (used for router type)
- Restores newCORSMiddleware function to api/rest/router.go for test compatibility
- Formats code with gofmt
2025-10-30 23:47:53 -07:00
igodwin 81d11e01bb Further auth and authz configuration 2025-10-30 23:35:28 -07:00
igodwin 52734efdec New doc supporting CORS, gRCP, and general client integration 2025-10-30 22:57:38 -07:00
igodwin 9a43af27ad Implement secure CORS configuration system 2025-10-30 22:28:03 -07:00
igodwin abe7b6beee Refactor auth and authz 2025-10-26 02:25:24 -07:00
igodwin 9ff782f7b6 Fix tls security issue 2025-10-26 00:28:53 -07:00
igodwin 6291cfe218 Fix unbounded memory growth in notification storage issue 2025-10-26 00:17:13 -07:00
igodwin a3365c303a Add API token auth and issues doc 2025-10-25 22:19:45 -07:00
igodwin 8cf369cc38 Fix Docker build by adding protobuf code generation step
The Docker build was failing because protobuf-generated code wasn't
  available during compilation. Changes made:
  - Added protoc and protobuf-dev packages to build dependencies
  - Installed protoc-gen-go and protoc-gen-go-grpc plugins
  - Added make proto-gen step to generate pb files before build

  🤖 Generated with [Claude Code](https://claude.com/claude-code)

  Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-22 23:46:47 -07:00
igodwin 35b6c8aed3 Enhance notification APIs with HTML email support, CC/BCC, and structured logging
Add comprehensive improvements across REST and gRPC APIs:
  - Add structured logging for all notification operations
  - Implement HTML email support with multipart/alternative MIME
  - Add CC and BCC recipient support for email notifications
  - Add GetNotifiers endpoint to query available notifier configurations
  - Support configurable From name in SMTP configuration
  - Auto-detect content type (text vs HTML) in notification bodies
  - Improve error handling and validation across all endpoints

  🤖 Generated with [Claude Code](https://claude.com/claude-code)

  Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-22 23:31:32 -07:00
igodwin 8229944149 Change logging and implement gRPC 2025-10-18 23:12:25 -07:00
igodwin 769fd5e2aa Set correct config type 2025-10-17 20:53:53 -07:00
igodwin aed1a3b6a1 Update go version in docker build stage to 1.24 2025-10-17 20:38:27 -07:00
igodwin c15f1a50ca Update config naming and doc 2025-10-17 20:36:29 -07:00
igodwin eb9e107f65 Add support for multiple instances of the same notifier type 2025-10-17 16:39:33 -07:00
igodwin ba3fad9431 Tidying up 2025-10-16 22:10:47 -07:00
igodwin d28bb92486 Update gitignore 2025-10-16 21:56:03 -07:00
igodwin 46b6008981 Fix build command in Dockerfile 2025-10-16 21:45:17 -07:00
igodwin 0749c6a6d3 Use ISO 8601 format for server logging 2025-10-16 21:28:38 -07:00
igodwin 9087a710e5 Basic impl added 2025-10-16 21:22:51 -07:00
igodwin 097ca99788 Update README.md 2024-11-01 20:00:32 -07:00
igodwin 46d3b66feb Add basic project layout 2024-11-01 19:36:58 -07:00
igodwin 7f867017fb Initial commit 2024-11-01 19:28:32 -07:00