Commit graph

2131 commits

Author SHA1 Message Date
6ef6b3d61d fix: comprehensive security audit fixes (#108)
This commit addresses multiple CRITICAL, HIGH, and MEDIUM severity security vulnerabilities identified in the security audit:

CRITICAL FIXES:
- Fix weak RNG for recovery codes - replaced Enum.random() with :crypto.strong_rand_bytes/1 for cryptographically secure token generation
- Fix subscription limit race conditions - moved free org and device quota checks inside transactions with FOR UPDATE locks to prevent concurrent bypass
- Fix default organization race condition - moved is_default check inside transaction to prevent multiple defaults per user

HIGH SEVERITY FIXES:
- Fix agent token deletion race condition - moved PubSub broadcast inside transaction to ensure agents only receive notification after successful deletion

MEDIUM SEVERITY FIXES:
- Fix LIKE wildcard injection in search - applied sanitize_like() to all user-facing search queries in devices.ex, sites.ex, and gaiia.ex to prevent enumeration attacks
- Fix Jason.decode! DoS - replaced with safe Jason.decode/1 with error handling in device_live/index.ex
- Fix SSRF vulnerability - added URL validation in HTTP executor to block requests to private/internal IP ranges (127.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16)
- Fix error information leakage - replaced inspect() in API responses with generic error messages, logging details server-side only
- Fix atom table pollution - HTTP method normalization now uses whitelist mapping instead of String.to_atom()

SECURITY IMPROVEMENTS:
- All quota checks now use pessimistic locking (SELECT FOR UPDATE) to prevent TOCTOU race conditions
- Private IP validation prevents cloud metadata service access (169.254.169.254)
- DNS resolution performed before HTTP requests to detect IP spoofing
- Error details logged server-side but not exposed to clients

Files changed:
- lib/towerops/accounts/user_recovery_code.ex
- lib/towerops/organizations.ex
- lib/towerops/devices.ex
- lib/towerops/sites.ex
- lib/towerops/gaiia.ex
- lib/towerops/agents.ex
- lib/towerops/monitoring/executors/http_executor.ex
- lib/towerops_web/live/device_live/index.ex
- lib/towerops_web/controllers/api/v1/mib_controller.ex
- lib/towerops_web/controllers/api/v1/agent_release_webhook_controller.ex
- lib/towerops_web/controllers/api/v1/geoip_controller.ex

Reviewed-on: graham/towerops-web#108
2026-03-22 10:10:27 -05:00
c8b275bd4c gleam4 (#105)
Reviewed-on: graham/towerops-web#105
2026-03-21 18:36:12 -05:00
e54ee75513 Rewrite protobuf encoding/decoding in pure Gleam with integrated validation (#104)
Replace the protobuf hex package (agent.pb.ex) and hand-written validator
(validator.ex) with a pure Gleam implementation: wire format primitives,
all 23 message types, encode/decode functions, and validation merged into
decode. Thin Elixir wrappers in agent.ex preserve the existing struct API.

- Wire format: varint, length-delimited, double (IEEE 754 via FFI), tag encode/decode
- Types: all proto3 message types as Gleam custom types with enums and oneof
- Decode: field-level parsing with integrated validation (UUID, IP, hostname, limits)
- Encode: proto3 default omission, bytes_tree concatenation, map/repeated fields
- Wrappers: Elixir structs with encode/decode delegating to Gleam, enum atom conversion
- Remove {:protobuf, "~> 0.12"} dependency

Reviewed-on: graham/towerops-web#104
2026-03-21 16:08:40 -05:00
348975dcc9 Rewrite polling_offset, changelog_parser, and user_agent_parser in Gleam (#103)
Migrate three pure-function modules from Elixir to Gleam:

- polling_offset: deterministic hash-based polling offset (pure Gleam, no FFI)
- changelog_parser: text parsing logic in Gleam, thin Elixir wrapper for File I/O
- user_agent_parser: regex-based UA parsing in Gleam with Erlang FFI for
  atom-keyed map conversion

Add gleam_regexp dependency for regex support in Gleam modules.
Update all call sites and tests to use Gleam module atoms.

Reviewed-on: graham/towerops-web#103
2026-03-21 10:11:06 -05:00
6859525a1b Integrate Gleam into the build pipeline and rewrite three pure-function modules (#98)
Set up mix_gleam archive, gleam_stdlib, and gleeunit deps. Add Gleam
compiler to all Dockerfiles, CI workflows, nix shell, and .tool-versions.

Rewrite Numeric, QueryHelpers, and URLValidator from Elixir to Gleam with
full ExUnit test coverage. Update all callers to use the Gleam modules
directly via :towerops@module syntax. Remove duplicate sanitize_like/1
private functions in snmp.ex and trace.ex.

Reviewed-on: graham/towerops-web#98
2026-03-21 07:30:36 -05:00
45a12ed181 Fix Check protobuf oneof field encoding for service checks (#101)
Protobuf oneof fields use a single :config key with tagged tuples
like {:dns, %DnsCheckConfig{}} rather than individual keys. The
check_type_config functions were setting e.g. dns: %DnsCheckConfig{}
directly, causing KeyError crashes in Check.__struct__/1.

Reviewed-on: graham/towerops-web#101
2026-03-21 06:56:48 -05:00
fc03ed7b0e Disable SSL for database connections on private network (#97)
Every timeout stack trace pointed at ssl_gen_statem.call/2 — the OTP 27
SSL gen_statem hanging during recv. PG is on private network (10.0.15.20)
and SSL verify was already disabled (verify_none), providing no security
benefit while adding CPU overhead and the gen_statem layer that caused
all connection timeouts.

Removed DATABASE_SSL and DATABASE_SSL_VERIFY env vars from both the
init container and main container. Also disabled SSL on the PG server
(ssl=off in postgresql.conf, hostssl→host in pg_hba.conf).

Reviewed-on: graham/towerops-web#97
2026-03-20 12:48:05 -05:00
8a54f9a866 Increase CPU limits to fix SSL connection timeouts (#96)
Pods were running at 500m/500m (100% of limit) with 44-47% of scheduling
periods being CPU-throttled. SSL decryption is CPU-intensive — when the
BEAM gets throttled mid ssl_gen_statem:call/2, the SSL recv stalls until
the cgroup allows CPU again, causing the 15s DBConnection timeout.

This was the actual root cause of "client timed out because it queued
and checked out the connection for longer than 15000ms" errors. Previous
fixes (TCP keepalive, pool tuning, oban pruner) were treating symptoms.

- CPU limit: 500m → 2000m (4 cores burst capacity)
- CPU request: 100m → 500m (guarantee adequate baseline)

Reviewed-on: graham/towerops-web#96
2026-03-20 12:24:06 -05:00
96995bd5aa Increase Oban Pruner throughput to prevent oban_jobs table bloat (#95)
Default Pruner settings (10k rows/30s = 333/s) can't keep up with job
completion rate (~600+/s from SNMP polling + sync workers), causing
oban_jobs to accumulate millions of completed rows. The bloated table
(47M rows, 29GB) makes Oban.Met.Reporter COUNT queries slow, blocking
on IO and holding DB connections until they timeout at 15s — the actual
root cause of the "ssl recv: closed" connection pool exhaustion.

Increase to 50k rows/5s (10,000/s) to keep the table small.

Reviewed-on: graham/towerops-web#95
2026-03-20 11:09:37 -05:00
071729f65c Reduce pool size and deployment surge to prevent PG connection exhaustion (#94)
PG max_connections=100 (97 usable). With pool_size=15 and maxSurge=100%,
rolling deploys briefly run 4 pods × 15 = 60 pool connections plus Oban
notifiers, exceeding the limit and causing too_many_connections errors
that cascade into ssl recv: closed timeouts.

- pool_size: 15 → 10 (steady state: 2×10=20, deploy: 3×10=30)
- maxSurge: 100% → 1 (max 3 pods during rollout instead of 4)

Reviewed-on: graham/towerops-web#94
2026-03-20 10:39:52 -05:00
b77950aed6 Fix persistent DB connection timeouts in k8s (#93)
- TCP keepalive more aggressive (14s detection vs 30s) so dead
  connections are found before the 15s checkout timeout fires
- Reduce pool size 30→15 per pod (2 pods = 30 total, avoids
  exhaustion during rolling deploys when 4 pods run briefly)
- Add idle_interval: 1s to proactively ping idle connections and
  detect dead SSL sockets before they're handed to real queries
- Simplify SSL config: remove redundant verify_fun and SNI disable
  when verify_none already skips certificate validation

Reviewed-on: graham/towerops-web#93
2026-03-20 10:19:43 -05:00
439c25ba9d Fix DB connection timeouts in k8s with aggressive TCP keepalive (#92)
SSL connections between pods and PostgreSQL are silently dying,
causing 15s checkout timeouts and cascading Oban failures.

- Set Linux TCP keepalive: 15s idle, 5s interval, 3 probes (30s detection)
- Add PostgreSQL statement_timeout (30s) to kill hung queries server-side
- Add disconnect_on_error_codes for fast recovery from PG shutdowns
- Bump default pool_size 20 → 30 to reduce contention under load

Reviewed-on: graham/towerops-web#92
2026-03-20 10:04:06 -05:00
8cf851d9e6 Add DB connection resilience settings (#89)
## Summary
- Add `queue_target` (2s) and `queue_interval` (5s) to Repo config so DBConnection proactively replaces stale connections when queries slow down
- Add TCP `keepalive: true` to prevent network intermediaries from silently dropping idle SSL connections

Addresses the `ssl recv: closed` errors crashing `Oban.Met.Reporter` in production.

## Test plan
- [x] Config-only change, compiles cleanly
- [x] Pre-commit hooks pass

Reviewed-on: graham/towerops-web#89
2026-03-19 18:33:02 -05:00
933bfafeb0 Fix DB pool exhaustion and settings page crashes (#88)
## Summary
- **DB pool exhaustion**: `device_has_effective_agent?` was doing 2 DB queries per call (3-table join + global settings lookup), called 2-3x per worker cycle across hundreds of devices (~6000 unnecessary queries/minute). Added an ETS-backed `AgentCache` with 120s TTL and removed redundant calls within the same worker cycle.
- **Settings page crash**: `KeyError: key :user_agent not found` on BrowserSession — field doesn't exist, replaced with `device_type`.
- **Login history IP truncation**: Changed from side-by-side grid to stacked table layout so IP addresses aren't clipped.

## Test plan
- [x] 8 new tests for AgentCache (cache hit, miss, invalidation, global default)
- [x] Full test suite passes (8651 tests, 0 failures, verified across multiple runs)
- [x] `mix format` and pre-commit hooks pass

Reviewed-on: graham/towerops-web#88
2026-03-19 15:16:34 -05:00
63fc09e58a fix check skip log noise and browser session crash (#87)
- Downgrade check executor skip log from info to debug to stop flooding
  production logs every 60s for every agent-assigned device check
- Fix KeyError on session.user_agent in settings page - BrowserSession
  schema has device_type field, not user_agent

Reviewed-on: graham/towerops-web#87
2026-03-19 14:29:39 -05:00
0c2c4cc354 gdpr (#86)
Reviewed-on: graham/towerops-web#86
2026-03-19 14:28:31 -05:00
8dae83a592 tweaks (#85)
Reviewed-on: graham/towerops-web#85
2026-03-19 13:51:02 -05:00
07fe6e6932 fix/subscriber-count-pluralization (#84)
Reviewed-on: graham/towerops-web#84
2026-03-19 12:14:50 -05:00
c18f034997 improve agent status display and fix two pre-existing test failures (#83)
- Wrap agent name + icon inside the offline amber badge so it's clear
  what is offline on the device detail page
- Add "Agent:" label before the agent indicator for all states
- Fix TimeSeriesTest deadlock: switch to async: false since check_results
  is a TimescaleDB hypertable and concurrent chunk creation deadlocks
- Fix unicode property test: String.length/1 counts grapheme clusters,
  not code points; combining chars can make a 2-codepoint string have
  only 1 grapheme, legitimately failing the min: 2 validation

Reviewed-on: graham/towerops-web#83
2026-03-19 11:43:42 -05:00
36cb080823 update deps (#82)
Reviewed-on: graham/towerops-web#82
2026-03-19 11:26:22 -05:00
2b50fc0732 fix agent check cascade and at-risk subscriber double-counting (#79)
## Summary

- **`list_checks_for_agent` cascade**: the agent query only matched checks with `agent_token_id` set directly on the check record — checks on devices inheriting their agent via site/org/global default were never sent to the Go agent. Now uses left joins with the full cascade (device assignment → site → org default → global default cloud poller), matching the pattern from `list_agent_polling_targets`.
- **At-risk summary double-counting**: `get_impact_summary` called `analyze_device_impact` per down AP, and each returned the full site subscriber count. With 3 APs down at a 147-sub site it showed 441 subs / $31k instead of 147 / $10k. Fixed by deduplicating impact per site. Same fix applied to `calculate_down_impact` in site impact summaries.

## Test plan

- [x] 3 new cascade tests for `list_checks_for_agent` (site, org default, global default)
- [x] 3 new tests for `get_impact_summary` (single site dedup, multi-site sum, zero case)
- [x] All 8,607 existing tests pass
- [ ] Deploy to staging, verify At Risk numbers on dashboard
- [ ] Verify Go agent receives DNS/HTTP checks via cascade

Reviewed-on: graham/towerops-web#79
2026-03-18 20:17:39 -05:00
d6f7dc570a simplify check skip logic to use runtime checks (#78)
## Summary
- Replace guard-clause-based `should_skip_snmp/service/ping_check?` with a single `should_skip_check?` function using runtime `in` operator checks
- Guard clauses with module attributes (`@service_check_types`) were not working in Dokku buildpack deployments despite code being correct locally
- Also update `CheckWorker` (dead code safety net) to check `device_has_effective_agent?`
- Use `Logger.info` for skip messages so they appear in staging logs

## Context
PR #77 introduced `device_has_effective_agent?/1` and updated all workers, but the guard-clause-based skip functions in `CheckExecutorWorker` weren't matching at runtime on staging despite passing all tests locally. The DNS check kept producing NXDOMAIN errors from Erlang's `:inet_res` resolver.

Root cause: The `when check_type in @service_check_types` guard clause compiled against module attributes appears to have stale evaluation in the Dokku Heroku buildpack release. Moving the check to a runtime function body fixes the issue.

## Test plan
- [x] All 230 worker tests pass
- [x] All 99 agents tests pass
- [x] Verified on staging: "Skipping check eceb3d51... (dns)" appears in info logs
- [x] No new NXDOMAIN results created after deploy

Reviewed-on: graham/towerops-web#78
2026-03-18 19:58:31 -05:00
f3cf1a9b3c skip all server-side checks when any agent is assigned (#77)
## Summary
- Add `device_has_effective_agent?/1` to Agents context that checks full cascade (device → site → org → global default) without distinguishing between cloud pollers and local agents
- Update check_executor_worker, device_poller_worker, and device_monitor_worker to use new function
- Phoenix never executes checks (DNS, ping, HTTP, TCP, SSL, SNMP) when any agent handles the device

## Problem
The global default cloud poller was not being checked in `should_phoenix_poll_device?/1`, causing Phoenix to run DNS/ping/service checks server-side even when the Go agent was handling the device. This produced duplicate results with NXDOMAIN errors from the Erlang DNS resolver.

## Test plan
- [x] 6 new tests for `device_has_effective_agent?/1` covering all cascade levels
- [x] Full test suite passes (8601 tests)
- [ ] Verify staging DNS check stops showing NXDOMAIN errors after deploy

Reviewed-on: graham/towerops-web#77
2026-03-18 17:29:36 -05:00
5493a83114 show decimal precision and units on graph y-axis (#76)
Chart.js was showing integer-rounded values like '2' instead of '2.1 ms'. Uses toFixed(1) in tick callbacks for proper decimal display with units.

Reviewed-on: graham/towerops-web#76
2026-03-18 15:49:20 -05:00
c4156ae334 fix graph 500 error on dns check page (#75)
Float.round/2 crashes on integer values from the status dataset. Adds integer guard clause to format_value/2.

Reviewed-on: graham/towerops-web#75
2026-03-18 15:33:29 -05:00
9d0c824aa5 add dual-axis graph for dns checks (#74)
DNS check graph now shows two datasets: Response Time (ms) on left y-axis and Status (Up/Down) on right y-axis with stepped green fill. Also adds space before units in chart labels.

Reviewed-on: graham/towerops-web#74
2026-03-18 15:13:31 -05:00
272d4bcdc9 fix: check state never persisted due to missing cast fields (#73)
Check.changeset/2 did not include state fields (current_state,
current_state_type, current_check_attempt, last_check_at, etc.) in
its cast list. Ecto silently dropped all state updates, so
update_check_state succeeded but wrote nothing to the database.

Added Check.state_changeset/2 for internal state machine updates,
keeping the regular changeset safe from user input. Updated
Monitoring.update_check_state/3 to use it directly.

Reviewed-on: graham/towerops-web#73
2026-03-18 14:54:52 -05:00
f05cc32ef6 fix dns/ping check execution and checks table alignment (#72)
## Summary
- **DNS checks**: `parse_server` returned a flat 5-tuple `{a,b,c,d,53}` instead of `{{a,b,c,d}, 53}` — the format `:inet_res` requires for nameservers. All DNS checks using a custom server failed with argument error.
- **Ping checks**: `System.cmd` `:timeout` option not supported on Elixir 1.19.4 (staging). Replaced with `Task.async`/`Task.yield` wrapper for cross-version compatibility.
- **Checks table UI**: Removed unnecessary flex div wrapper on status badge column that caused vertical misalignment with other table cells.

## Test plan
- [x] DNS executor tests pass (custom server resolution now succeeds)
- [x] Ping executor tests pass (no more ArgumentError)
- [x] Full test suite passes (8593 tests, 0 new failures)

Reviewed-on: graham/towerops-web#72
2026-03-18 14:05:37 -05:00
997bc46cbf fix: dns and ping checks not executing in production (#71)
## Summary

- **Ping checks never scheduled**: `ensure_default_ping_check` created check records but never called `schedule_check`, so auto-created ping checks never executed
- **Skipped checks permanently orphaned**: `CheckExecutorWorker` only rescheduled checks that actually executed — skipped checks (agent-assigned, SNMP disabled) broke the scheduling chain permanently
- **No recovery mechanism**: `JobHealthCheckWorker` only recovered `DeviceMonitorWorker` jobs, not `CheckExecutorWorker` jobs — any lost check job was gone forever

## Changes

- `monitoring.ex`: Call `schedule_check` after creating ping check in `ensure_default_ping_check`
- `check_executor_worker.ex`: Reschedule skipped-but-enabled checks so conditions are re-evaluated next cycle
- `job_health_check_worker.ex`: Recover orphaned `CheckExecutorWorker` jobs for enabled checks (runs every 10 min)
- `devices.ex`: Wrap `ensure_default_ping_check` in try/rescue for deadlock resilience

## Test plan

- [x] New tests: ping check scheduling on creation, no duplicate scheduling on idempotent call
- [x] Updated tests: skipped checks now assert rescheduling instead of orphaning
- [x] New tests: health check worker recovers orphaned check executor jobs, skips disabled checks
- [x] All 177 targeted tests pass
- [x] Compile clean with warnings-as-errors
- [x] Pre-commit hooks pass (credo, format)

Reviewed-on: graham/towerops-web#71
2026-03-18 08:43:37 -05:00
c8a092196f add auto ICMP ping check for all devices (#68)
## Summary

- Every device now automatically gets an ICMP ping check (`source_type: "auto_discovery"`) when created via `Monitoring.ensure_default_ping_check/1`
- When a device IP changes, the ping check host config is synced automatically
- Backfill migration inserts ICMP Ping checks for all existing devices that don't have one
- Fixes checkbox alignment in the Add Service Check modal (verify SSL / follow redirects)

## Test plan

- [x] 4 new tests for `ensure_default_ping_check/1` (create, idempotent, IP update, no-op)
- [x] 3 new tests for device lifecycle (auto-create on device create, sync on IP change, no-op when IP unchanged)
- [x] 13 existing tests updated to account for auto-created ping check
- [x] Full test suite passes (`mix precommit`)
- [ ] Verify backfill migration on staging
- [ ] Create device in UI, confirm ping check appears in service checks list
- [ ] Verify checkbox alignment in Add Service Check modal

Reviewed-on: graham/towerops-web#68
2026-03-17 17:55:23 -05:00
3c935ed56e e2e-workflow-separate (#67)
Reviewed-on: graham/towerops-web#67
2026-03-17 16:30:45 -05:00
f897b32999 Separate E2E tests into manual workflow (#66)
Moves e2e tests from production workflow into a separate workflow that requires manual triggering. This allows e2e tests to be run on-demand without blocking production deployments.

Changes:
- Created new .forgejo/workflows/e2e-tests.yaml with workflow_dispatch trigger
- Removed disabled e2e job from production.yaml workflow
- E2E tests can now be triggered manually from Actions tab with browser selection (all, chromium, firefox, webkit)
- Test results and videos uploaded as artifacts on all runs

Reviewed-on: graham/towerops-web#66
2026-03-17 16:26:41 -05:00
0f9ce9b711 feat/ssl-certificate-check (#65)
Reviewed-on: graham/towerops-web#65
2026-03-17 16:21:44 -05:00
76b3dd6a93 add ssl/tls certificate expiration check type (#64)
add ssl check executor that connects via TLS, reads peer certificate
expiry, and returns OK/WARNING/CRITICAL based on configurable
warning_days threshold. includes protobuf config, liveview form fields,
agent channel encoding, oban worker dispatch, and unit tests.

Reviewed-on: graham/towerops-web#64
2026-03-17 16:14:22 -05:00
150ffb421d delegate service checks to agents and fix ui issues (#63)
- Skip HTTP/TCP/DNS checks in Phoenix when device is assigned to agent
- Agents now execute service checks from the device's network location
- Fix cancel button in service check modal (text link instead of button)
- Fix status badge alignment in checks table

Reviewed-on: graham/towerops-web#63
2026-03-17 15:43:31 -05:00
b724aa4ab8 remove docker build cache options for docker driver (#62)
The docker buildx driver (required on unprivileged ci runner) does not
support registry cache export/import. Remove cache-from and cache-to
options to unblock builds.

Reviewed-on: graham/towerops-web#62
2026-03-17 13:40:54 -05:00
f4e75689d4 fix docker on unprivileged ci: use vfs storage driver (#61)
overlayfs requires mount/unmount capabilities not available on the
unprivileged ci runner. Switch to vfs storage driver and use buildx
docker driver (not docker-container) to avoid nested mount issues.

Reviewed-on: graham/towerops-web#61
2026-03-17 13:34:53 -05:00
cd19db0680 fix-sticky-header-overlap (#60)
Reviewed-on: graham/towerops-web#60
2026-03-17 13:21:58 -05:00
b03f280b65 fix-ci-docker (#59)
Co-authored-by: Forgejo Actions <actions@git.mcintire.me>
Reviewed-on: graham/towerops-web#59
2026-03-17 13:13:09 -05:00
fa2e09d7cc skip discovery for devices with snmp disabled (#58)
add early bail in DiscoveryWorker when snmp_enabled is false.
mark snmp_not_enabled and device_unresponsive as non-retryable
errors to prevent wasted retries.

Reviewed-on: graham/towerops-web#58
2026-03-17 11:16:33 -05:00
f807eb1e78 fix: add Docker daemon startup to production workflow (#57)
The new CI server doesn't have Docker daemon running by default,
causing buildx setup to fail with:
  ERROR: failed to connect to the docker API at unix:///var/run/docker.sock

This fix:
- Adds explicit Docker daemon startup check and initialization
- Configures buildx with docker-container driver
- Adds install: true to ensure proper buildx installation

This was already fixed in build-deploy.yml (commit 54280fd7) but
was missing from production.yaml.

Reviewed-on: graham/towerops-web#57
2026-03-17 11:02:49 -05:00
0c8bb35ef7 add query_helpers tests and fix all credo issues (#56)
- add tests for QueryHelpers.sanitize_like/1 (%, _, \ escaping)
- fix nesting depth in store_check_result, handle_check_state_change,
  graphql check resolver, checks controller, and ping executor
- fix cyclomatic complexity in build_check_protobuf by extracting
  check_type_config/2 function clauses
- fix formatting in api_docs_html template

Reviewed-on: graham/towerops-web#56
2026-03-17 10:43:57 -05:00
1781aace98 fix failing tests: ping check config and whitelist cache race (#55)
- check_results_controller_test: add required config host for ping check
- brute_force_protection_test: set async: false to avoid ETS cache race

Reviewed-on: graham/towerops-web#55
2026-03-17 08:11:36 -05:00
28e97ff5f0 add service checks REST API, GraphQL API, and ping executor (#52)
- REST CRUD at /api/v1/checks for HTTP, TCP, DNS, ping checks
- GraphQL queries (checks, check) and mutations (create/update/delete)
- ping executor using system ping with macOS/Linux parsing
- check config validation for ping type (requires host)
- API documentation updated with checks resource section

Reviewed-on: graham/towerops-web#52
2026-03-16 18:40:18 -05:00
d0e2ead923 hide rf links from sidebar navigation (#51)
Reviewed-on: graham/towerops-web#51
2026-03-16 17:54:57 -05:00
c6913685ee wire up service checks (HTTP/TCP/DNS) to remote agents (#50)
send check_jobs protobuf to agents during job dispatch cycle, handle
check_result messages back with state transitions and alert
creation/resolution. auto-assign checks to device's effective agent
token on creation, broadcast check changes via PubSub so connected
agents get immediate updates.

add edit/delete support for service checks on device show page with
form pre-population from existing check config. auto-fill TCP host
from device IP address.

includes validate_check_result/1 for protobuf validation and
list_checks_for_agent/2 for querying agent-assigned service checks.

also fixes pre-existing broken device_type_icon reference in device
index template.

Reviewed-on: graham/towerops-web#50
2026-03-16 17:25:02 -05:00
06c5d14455 ui/remove-device-type-column (#49)
Co-authored-by: Forgejo Actions <actions@git.mcintire.me>
Reviewed-on: graham/towerops-web#49
2026-03-16 16:38:00 -05:00
c5481c2cd3 add mobile token auth to GraphQL endpoint (#48)
Extend the GraphQL API and socket to accept mobile session tokens
alongside existing API tokens. Add OrganizationScope middleware
that extracts organization_id from query args for mobile users.
Add my_organizations query for mobile app org listing.

Reviewed-on: graham/towerops-web#48
2026-03-16 15:23:07 -05:00
0fa1fa800b fix/suppress-health-check-logs (#47)
Reviewed-on: graham/towerops-web#47
2026-03-16 15:22:12 -05:00
26c7b0b993 Hide RF Links UI elements from network map (TOW-44) (#46)
- Commented out 'View RF Links' button in network map node detail panel
- Commented out RF Link health legend (Good/Degraded/Critical) from map legend
- Backend code (topology.ex) left intact per instructions
- No RF tab found on device show page (nothing to hide there)
- All tests pass, compiles clean with --warnings-as-errors

Reviewed-on: graham/towerops-web#46
2026-03-16 15:19:03 -05:00