| Version | Supported |
|---|---|
| Latest release | ✅ |
| Previous release | ✅ (critical fixes only) |
| Older releases | ❌ |
Only the two most recent releases receive security fixes. If you are running an older version, upgrade before reporting.
Please do not file a public GitHub issue for security vulnerabilities.
Report vulnerabilities privately using one of the following channels:
- GitHub Private Vulnerability Reporting — preferred. Open a draft security advisory at
Security → Report a vulnerabilityin this repository. - Email — if you cannot use GitHub's private reporting, email the maintainers directly. Find the contact address in the repository's GitHub profile.
Include as much detail as you can:
- A clear description of the vulnerability and the affected component
- Steps to reproduce or a proof-of-concept (a minimal reproducer is better than a full exploit)
- The potential impact and attack surface (e.g., local privilege escalation, credential exposure, remote code execution)
- The platform(s) and version(s) affected
- Any suggested mitigations if you have them
| Milestone | Target |
|---|---|
| Acknowledge receipt | 2 business days |
| Confirm or refute the report | 5 business days |
| Share a remediation plan | 10 business days |
| Release a fix (for confirmed critical/high findings) | 30 days |
We will keep you informed at each step. If a deadline cannot be met we will tell you why and give an updated estimate.
We follow coordinated disclosure. We ask that you give us reasonable time to develop and ship a fix before any public disclosure. Once a fix is released we are happy to credit you in the release notes and in this file (if you wish).
The following are in scope:
- The SentinelGo agent binary and all packages under
internal/andcmd/ - The installation scripts under
installation-doc/ - The build and release pipeline (
.github/workflows/)
Out of scope:
- The Supabase platform itself (report those to Supabase)
- GitHub infrastructure
- Third-party dependencies — report those to their respective maintainers; we will track the fix and update our dependency
SentinelGo runs as a privileged system service (systemd / launchd / Windows SCM) and communicates exclusively with a Supabase backend. The points below describe the security controls built into the agent. The threat model and the reasoning behind these controls are in the assurance case.
SentinelGo is designed to protect:
- the agent's credentials and the data it collects, in transit (HTTPS only) and at rest on the device (owner-only file permissions);
- the agent binary itself: updates are installed only if they are newer, match the published SHA-256 checksum and carry a valid Ed25519 signature;
- the device from the agent's own mistakes: input from the OS and the network is validated and fuzzed, external commands are run by absolute path, and task output is redacted before upload.
SentinelGo does not protect against:
- A compromised backend. The backend can send tasks that run scripts with the agent's privileges (root or SYSTEM). Anyone who controls your Supabase project or its service-role key controls every enrolled device. Protect the backend accordingly.
- A local administrator or root user. They can read the agent's config, including its credentials, and stop or replace the agent.
- A compromised device. SentinelGo reports a device's state; it is not an antivirus or EDR and does not prevent attacks on the device.
- Every agent authenticates to the backend using a unique
agent_uuid/agent_secretpair. These are never derived from hostnames or other guessable data. - On successful login the agent receives a short-lived JWT. The JWT is automatically refreshed before expiry; the agent never relies on a single long-lived token.
- A circuit breaker (
internal/resilience/) stops repeated failing calls and backs off, so a misconfigured or rejected agent doesn't hammer the backend. - The agent secret and tokens are stored in cleartext in
config.json, protected by file permissions: mode0600in an owner-only0700directory on Linux and macOS (internal/config/secure_unix.go), and an explicit DACL allowing only SYSTEM, Administrators and the agent's account on Windows (internal/config/secure_windows.go).
- The agent only talks to the Supabase backend, over HTTPS. A
supabase_urlthat isn'thttps://is rejected at startup, so there is no plaintext or downgrade path. - TLS uses Go's
crypto/tlsdefaults: TLS 1.2 or newer, certificate verification always on. - The shared HTTP client (
internal/httpx/) enforces timeouts on every outbound request.
- Script payloads are downloaded exclusively from a Supabase Storage bucket that is protected by Row Level Security. The agent uses its authenticated JWT for every download; unauthenticated requests are rejected by the backend.
- The task runner (
internal/service/task/) does not evaluate or interpolate the script path — it downloads to a temporary file and executes it with the OS shell. Scripts larger than 10 MiB are rejected. - Scripts run with the agent's privileges. This is by design (they exist to administer the device), and it is why the backend must be trusted; see "What to expect" above.
- Execution results (exit code, stdout, stderr) are uploaded back to the backend. The sanitize package (
internal/sanitize/) redacts PII and credential-like patterns from outputs before they leave the machine.
- Release binaries are cross-compiled with
CGO_ENABLED=0(fully static, no C runtime dependency) and are reproducible; seeRELEASE.md. - Every release binary and installer is signed with Ed25519 (
scripts/sign), listed inSHA256SUMS, and has a GitHub build-provenance attestation. - The updater (
internal/updater/) asks the backend for the latest release through theget_latest_agent_releaseRPC and downloads the binary and its.sigfrom Supabase Storage. It installs the update only if:- the version is strictly newer than the running one (no downgrades),
- the SHA-256 matches the release manifest, and
- the Ed25519 signature verifies against the public key compiled into the agent (
internal/updater/pubkey.go). A missing or invalid signature is a hard failure.
- The current binary is backed up first, the new one replaces it atomically, and a failed replace rolls back to the backup.
- The installers verify the binary against
SHA256SUMSand refuse to install if the file is missing. Seeinstallation-doc/INSTALLATION.md.
- The audit log collector (
internal/auditlogs/collector/) reads from OS-native event sources (Windows Event Log, Linux journald, macOS unified log) using read-only APIs. It does not modify system logs. - Events are queued in a local SQLite database (
internal/store/) before upload. The queue provides at-least-once delivery without losing events across restarts.
- The lockfile module (
internal/lockfile/) enforces single-instance execution. The lock key includes the agent UUID and binary version, so an in-place upgrade does not block the new process from starting. - The agent drops no privileges on startup; it runs at the level required by the service manager. Avoid running as
rootorSYSTEMunless the installation guide specifically requires it for a feature on your platform.
Every pull request and release runs:
| Check | Tool | Gate |
|---|---|---|
| Static analysis | CodeQL | Merging to main is blocked on high-severity alerts |
| Static analysis | Semgrep | Required check |
| Static analysis and coverage | SonarCloud | Quality gate reported on every pull request |
| Static analysis | gosec | Findings uploaded to the GitHub Security tab (advisory only) |
| Secret scanning | GitGuardian | Required check |
| Dependency and Go toolchain vulnerability scan | govulncheck |
Pull requests and releases fail on any reachable vulnerability. Each release binary is scanned before signing, and a weekly scan checks main and the latest release |
| Filesystem/secret/misconfiguration scan | Trivy (CRITICAL + HIGH) | Build fails |
| Module integrity | go mod verify |
Build fails on tampered modules |
| CGO enforcement | Custom grep | Build fails if import "C" is introduced |
| Lint (all three OSes) | golangci-lint | Build fails |
| Fuzzing | Go native fuzzing | Nightly, on every parser of untrusted input |
Release binaries are produced only after the full CI pipeline passes. No binary is attached to a GitHub Release unless all gates are green. The main branch only accepts changes through pull requests that pass the required checks, with no bypass for anyone.
The following gaps are acknowledged and tracked in the roadmap:
- Update rollback after a failed start — A failed binary replace rolls back automatically, but if the new binary is installed and then fails to start, there is no automatic rollback. Recovery means reinstalling a previous release.
- Cleartext credentials on disk — The agent secret and tokens are protected by file permissions, not encrypted with an OS keychain.
- No mTLS — The agent authenticates to Supabase via JWT, not mutual TLS. This is an inherent constraint of the Supabase Edge Function API.
We thank everyone who has responsibly disclosed security issues to us. Contributors will be listed here with their consent.