dcj · dotcomjack

GruxKit

Guardrails for desktop AI agents, in Swift. Secret redaction and SSRF policy for anything that reads your screen and follows links.

MIT Swift 5.9 macOS 13+ 0 dependencies 96 tests
00
days
00
hrs
00
min
00
sec

Public Saturday, August 15, 9:00 AM ET

Public Saturday 9:00 AM ET

Add to Package.swift

.package(url: "https://github.com/dotcomjack/grux-kit.git", from: "0.5.0")

Your own manifest needs platforms: [.macOS(.v13)] too. Leaving it out is a build failure, not a warning.

See it work

An agent that can see your screen will eventually see a secret

The moment that text is interpolated into a prompt, it leaves your machine. Edit the left pane and watch what would reach the model.

What the agent read

What reaches the model

This demo is a JavaScript approximation, not the library. The real matcher is the Swift one: twenty four patterns plus a generic high entropy pass, table driven and pinned by tests. It is reproduced here loosely so the idea is visible in two seconds. A security tool whose demo quietly disagrees with its own library is exactly the failure this library exists to complain about, so it is worth saying plainly rather than in a footnote.

Two modules

SecretRedactor

Twenty four patterns plus a generic high entropy pass. Two properties are load bearing and both are pinned by tests.

Most specific wins. A Stripe live key is tagged [REDACTED:STRIPE_LIVE_SECRET], not the generic entropy tag. Precision is what makes the audit trail worth reading later.

It is idempotent. redact(redact(x)) == redact(x). Prompts get assembled from fragments that were each cleaned on the way in, so the function runs over its own output constantly.

PEM blocks are consumed whole, header through footer, including a truncated block with no footer. Redacting the header alone would tag the block and then hand the model every byte of the key.

What it does not do

It is a matcher, not a parser, so it cannot catch a secret that does not look like one. Single case hex strings are deliberately exempt, which keeps git SHAs and checksums intact but lets a 32 or 64 character lowercase hex API secret through untouched. That is a real gap, not just a feature, and you should know which side of it you are on.

URLGuard

The threat is server side request forgery with a language model as the confused deputy. Your agent runs on your laptop, inside your network, and it will follow a link that came from a web page, an email, or its own hallucination.

Default posture: http and https only, credential bearing URLs always denied with no override, and loopback, private ranges, link local, carrier grade NAT, .local and bare single label hostnames all denied.

Every denial carries a tag, and the tag list is complete on purpose. A test fails the build if the code can emit a tag the table does not name. Alert loudest on HOST_SMUGGLING, the only tag that implies intent.

What it does not do

It does not follow redirects, and that is the biggest gap. evaluate judges one string, and a perfectly public URL is free to answer 302 Location: http://127.0.0.1:8080/. You must re-evaluate every hop. It does not resolve DNS either, so rebinding is outside what it can see.

Every tag before 0.5.0 leaks credentials

And each one looked fine when it was cut

Published tags stay resolvable so existing checkouts do not break. They are documented here and in the changelog so nobody adopts one by accident. Use 0.5.0.

TagWhat it actually does
0.1.0Passes private key bodies straight through to the model, and the injection fence is forgeable.
0.2.xLeaks the AWS secret access key.
0.4.0Measured against a real build rather than inferred from its changelog: allows the loopback and NAT64 SSRF bypasses, and leaks indented PEM bodies, PGPASSWORD=, session cookies, Set-Cookie, bare Bearer headers and curl -u passwords.

The one to check if you have ever pinned it.

In 0.4.0, a denylist entry written any way other than a bare host matches nothing at all. https://evil.com, evil.com:443 and *.evil.com are all silently inert against denylist: ["evil.com"]. Your own denylist fails open while looking configured.

Five tags published, all five later found to leak, several by audits of code that had already survived earlier ones. 0.5.0 is the most heavily audited state this library has been in, and that is a statement about effort, not a guarantee. Pre 1.0, so treat the minor version as breaking.

Roadmap

In the order they are coming out of the private codebase

Each one lands with its tests rather than as a sketch. Nothing below is shipped.

Tiered approval not shipped

Green, yellow and red action classes. What runs silently, what asks first, what is never automated.

Cost metering not shipped

Per call accounting and budget ceilings, because an agent in a retry loop is a billing incident.

Swarm orchestration not shipped

Fan out to parallel workers with structured results.

Voice hallucination guard not shipped

Near silent audio makes Whisper emit confident garbage. The hard constraint is the false positive side: it must never reject ordinary dictated speech.