Red Kite / Security
An agent on eighty customer servers is a significant piece of infrastructure.
It deserves to be treated as one. This page is the whole position, written plainly enough that a suspicious engineer can check it against the script we ask you to install — which is a shell script, and which you can read.
The agent never accepts instructions
This is the most important decision in the product, and the one most likely to be argued with. The agent sends; it does not receive anything but a suggested check-in interval and, occasionally, a replacement credential. No remote command execution, no “run this script”, no self-update that fetches code.
The reasoning is arithmetic rather than caution. An agent that can be told what to run turns one compromised hub into administrative access to every customer site simultaneously. That is not a monitoring system, it is a botnet with a support contract.
The agent’s entire surface
One route, outbound, initiated by the agent:
The whole of it
POST /api/v1/check-in Authorization: Bearer rk_live_…
There is no GET /api/v1/config, no GET /api/v1/commands, no polling for work. The suggested interval rides back on the check-in response, which is precisely why it is put there. Every additional agent-facing route is surface area on the thing this rule protects — if one were ever genuinely needed, that would be a decision taken deliberately and written down, not a convenience.
Storing a value is not the same as obeying an order
Tokens must be rotatable without an engineer visit, so the reply does carry more than an interval. The line is not how much comes back. It is what the agent does with it.
| The reply may carry | Because the agent | Allowed |
|---|---|---|
| A suggested interval, in seconds | writes it to a config value | Yes |
| A replacement token, an opaque string | writes it to a config file | Yes |
| A URL to fetch | would have to go and get something | No |
| A script, command, path or package version | would have to run something | No |
| A flag changing which figures are gathered | would be choosing its behaviour from the network | No |
The test: could a malicious hub response cause the agent to execute code, read a file it does not already read, or contact a host other than the configured hub? If yes, it is an instruction, whatever it is called. An integer and an opaque credential fail that test safely; anything with a scheme, a slash or a shell metacharacter in it does not.
So the agent validates what it stores: the interval is a bounded integer, and a replacement token must match the expected prefix and character set before it is written. An agent that writes an arbitrary string from the network into a file it later reads is an agent that accepts instructions by accident.
What the agent refuses to do
- No eval, source, bash -c or Invoke-Expression applied to anything from the response.
- No part of a response is written to a file that is later executed.
- No HTTP request to any host that did not come from local configuration.
- No self-update, auto-update, or version check that downloads.
- No privilege escalation. Readings that need root are taken by a separate root-owned unit with PrivateNetwork=yes, which cannot speak to anything at all. There is no sudoers line and no setuid bit.
And in the hub: no endpoint the agent would call with PUT, PATCH or DELETE, and no “command queue”, “pending actions” or “agent tasks” table. The name will always be innocent; the thing will not be.
The hub never connects to a monitored machine
Push, never poll. The agent reaches out; the hub listens. Every other benefit follows from it: no inbound firewall rule, no VPN, no port forwarding, nothing to expose at your site.
| Direction | Allowed | Example |
|---|---|---|
| Your machine → the hub | Yes — the only channel | POST /api/v1/check-in |
| The hub → your machine | Never | — |
| The hub → a third-party service | Yes | the email, SMS and push providers; the external dead man’s switch |
The rule is narrower and sharper than “the hub makes no outbound connections”. It makes several, and one of them is required — the hub pings a third-party dead man’s switch every few minutes precisely so that somebody who is not the hub itself notices when it dies. The rule is that nothing the hub does may require a route into a customer’s network.
You can check this from the outside: the hub holds no address for your machines. There is no field for an IP address, an SSH port or a management URL, because storing one would be the first step towards dialling it.
Tokens
- One per machine, never shared. A leaked token compromises one machine’s readings, and is revoked from the hub without visiting the site. A single token shared across a fleet cannot be revoked without visiting all of them.
- Hashed at rest, like a password. A hub database that leaked must not hand over working credentials. Not even the hub can tell you a token after it has been issued — it can only replace it.
- Prefixed and identifiable — rk_live_… — so one found in a paste, a log or a screenshot is recognisable on sight.
- Compared in fixed time, so the comparison itself leaks nothing.
- Never in a URL or a query string. An Authorization header only: web servers, proxies and browser history all log URLs.
- Rotatable without an engineer visit, with the old token valid for an overlap so a rotation cannot strand a machine.
- An unknown or revoked token gets a 401, and the attempt is recorded. Repeated attempts from a revoked token are worth knowing about.
What a check-in may contain
Figures, never content. Anything proposed for the payload is judged by one question: if this database leaked, what would the customer mind?
- No file names, user names, directory listings or process command lines.
- No kernel or event log message text — only how many there were.
- No drive serial numbers. They identify a specific asset and nothing on the hub needs one.
- No network traffic figures. How much data you move is your business; error counters are a health figure, and that is all that travels.
The published payload on the front page is the whole of it, and what we watch lists every figure with the reason it is there.
Keeping customers apart
Multi-tenant from the first line of code, because retro-fitting tenancy is a rewrite and designing for it costs nothing. Every query that touches a machine, check, check-in, incident, notification, site, token, threshold or maintenance window is filtered by the set of customers the caller may see. There is no exception for admin screens, no exception for exports, and no exception for “it is only a count”.
A count, a chart, a CSV and a JSON list are all queries, and a leak through an aggregate is still a leak — “you have 4 machines, the system has 631” tells a customer more than it should.
Permissions are data rather than hard-coded conditionals: endpoints declare what they need, roles give defaults, and per-user overrides sit on top. A rule of this kind must be tested rather than trusted, so a test reads the route table itself and fails the build when a route forgets its permission. The one route somebody adds in a hurry is the one that matters.
The hub itself
- Public on the internet by necessity, because agents must reach it. TLS with a real certificate, and no administrative interface reachable without authentication.
- The management interface and the check-in endpoint sit on separate paths with separate limits, and the check-in limit is enforced per machine token rather than per IP address — a hundred agents behind one office’s single address are a hundred separate quotas.
- A machine in a crash loop cannot fill the database. Rate limited per token, and a hard ceiling on request body size at the edge so a hostile agent cannot post a hundred megabytes at the endpoint everybody else depends on.
- Full audit trail on every human action: append-only, never updated, never deleted.
- Backups from day one, with the key kept apart from the archives. A backup you have never restored is not a backup, so the restore is tested rather than assumed.
- Nothing is hard-deleted. Incidents and notifications are the record of what the service did and did not do.
When somebody asks for remote access
They will, and it is a genuinely useful feature. The answer is that it belongs in a separate tool — with its own authentication, its own audit trail and its own explicit customer consent — not bolted onto the one piece of software that is allowed to run unattended on every machine you own.
We would rather lose that sale than ship the thing that makes every other customer’s network reachable from one compromised server of ours.
Telling us about a problem
If you find something wrong — in the agent, the hub, or this page — write to contact@redkite.info and say so plainly. We would far rather hear it from you than from a customer.
You are welcome to read the agent before it goes anywhere near a customer’s server. It is a shell script on Linux and PowerShell on Windows, deliberately: no compiled binary, no runtime to install, and nothing you cannot audit at three in the morning without a build machine.
It comes with the licence, in full and unobfuscated — there is no version of this product where you are asked to run something you are not allowed to read. And if your policy is that nothing unread goes on a customer’s machine, say so while you are evaluating and we will send it over before you buy rather than after. That is a normal request from a serious buyer and we have never minded it.