> ./OMNISIGHT
A self-hosted endpoint network visibility and SIEM-oriented product: an endpoint agent, a local server, an operator console, installers, and a cloud control plane for licensing and updates. The interesting part is not its size. It is the boundaries I committed to before writing the first line, and what holding them cost.
What it does
Agents on managed endpoints report connection metadata, software inventory and system metrics to a server the customer runs on their own hardware. The server stores it, runs detection against it, matches installed software to public vulnerability data, and drives an operator console for search, alert triage and reporting.

Endpoint agent
Go. Network metadata, inventory, heartbeat, aggregate metrics.
Local server
Python API, relational store, RBAC, audit, detection, reporting.
Operator console
React. Dashboard, event search, alert triage, vulnerabilities.
Control plane
Licensing, update delivery, rollout state, aggregate report relay.
Vulnerability service
CVE feed sync and version-aware matching against inventory.
Installers and packaging
macOS and Linux install, update, and uninstall flows.
The three boundaries
A monitoring product is, by construction, the thing with the most access on the network. That makes it a liability as much as a tool, and the design questions worth answering are about what it deliberately refuses to do.
Metadata only, as a contract
No packet payloads, request bodies, headers, cookies, decrypted TLS, command lines, endpoint files, or credentials. This is written into the product's terms, not just the docs — so widening it later is a contractual change with a correction sequence, not a feature flag.
Outbound only
The cloud never opens a connection into a customer network. The customer's server initiates every exchange: licence checks, update polls, relay sends. A compromise of my infrastructure gives an attacker no path inward.
Fail closed, everywhere
The email relay rejects any recipient absent from its allowlist rather than queueing it. Vulnerability matching declines to guess when it cannot resolve a version. Runtime gates refuse to start rather than starting degraded.

Where the data actually goes
Telemetry stays on the customer's server. What crosses to my infrastructure is a short, deliberate list, and every item on it is there because the feature is impossible without it — software names and versions to match vulnerabilities, aggregate summaries to deliver a report, licence and update requests. The asymmetry between the two lists is the design.
Two decisions worth defending
Durable data goes over REST, never the socket
The console updates live, which usually means pushing data over a WebSocket. I push notifications that carry no payload at all — a signal that something changed, after which the browser re-fetches over the authenticated REST path. It is more work and one extra round trip. It also means the socket never becomes a second, less-audited way to read data, and that authorization is enforced in exactly one place.
The lab outage I wrote up separately is the counterweight to that decision: because the socket and REST do not share a failure domain, the socket kept reporting healthy while REST ingest was entirely blocked. Splitting them bought me a cleaner authorization story and cost me a monitoring blind spot. Both are true.
I withdrew my own release
During a security review of my own installer I found a flaw serious enough that shipping past it was not defensible. I pulled the published artifact rather than patching forward quietly, fixed the cause at the source, and left it unpublished until it could be rebuilt and reviewed properly. Publishing a new version to cover an old one leaves the old one installed on machines; withdrawing it does not.
What I would do differently
I let the documentation grow without a structure. The always-loaded set eventually reached tens of thousands of tokens because three separate documents each accumulated their own copy of the same release history, and it became genuinely unusable — for collaborators and for me. Fixing it meant separating documents that describe the present from a changelog that describes the past, and enforcing that split with a check rather than a convention.
The general lesson is the one I keep relearning: a rule nobody measures is a rule that has already been broken. Every boundary on this page has something that fails when it is crossed — a gate, a test, a script — because the ones that only existed as intentions did not hold.
> Design the refusals first. The features will follow the shape they leave.
Deciding early what the product would never collect made most later decisions easy, because anything that needed content was simply not on the table.