---
title: "Security - how Harbyn keeps your code on your side | Harbyn"
description: "Harbyn's security model: analysis runs on your machine or CI, the change feed is signed with Ed25519, model output is validated, and fixes are pull requests you review."
canonical: "https://harbyn.com/security"
last-updated: 2026-10-25
---

# Built so your code never reaches us

Markdown version of <https://harbyn.com/security>.

Harbyn reads untrusted text from the internet and proposes changes to other people's code. This page describes how it is built so that neither of those becomes a way into yours.

## Your code stays with you

Your source code never touches Harbyn's servers.

Everything that reads code (scanning, fixes, SDK migrations) runs on your machine or your CI runner, in the open-source CLI and GitHub Action. Harbyn's servers publish change data, which is public, and hold no copy of your code.

- A scan makes one network request: it downloads the signed change feed. Nothing about your code is sent, and there is no telemetry.
- Harbyn holds no write access to your repositories. Fix and migration pull requests are opened by your own workflow, with your job's token.
- Connecting GitHub in the dashboard installs a GitHub App with one permission: read repository metadata. It cannot read your code.
- The scanner never opens `.env` files, private keys, `.npmrc`, `.netrc` or credential files, and never follows symbolic links out of the directory it scans.

The data Harbyn holds is not worth much to an attacker by design: if its servers were breached, there would be no customer code to take.

## What the cloud holds

| Data | What it is |
| --- | --- |
| Change feed | Vendor changes, their dates and their sources. Public, and published to everyone. |
| Account | Your email address and name (plus a profile picture link if you sign in with GitHub or Google), your alert settings and your plan. Payments go through Stripe's hosted checkout; card data never reaches Harbyn. |
| Repository connections | Only if you connect a repository: its name (`owner/repo`) and, for each upload, the commit, ref, run URL and counts. |
| Uploaded scan results | Only if your workflow sets `upload: true`: feed event ids, the feed terms that matched, where they matched (code, tests, docs) and counts. No paths, no line numbers, no code. |

- The upload accepts only event ids, vendor ids and identifiers that exist in the public feed, plus numbers. Even a tampered upload cannot make Harbyn store a string from your code.
- The Action prints the exact body it sends in the job summary before sending it, and sends it to one fixed address.
- Uploads carry no secret of ours. GitHub signs a short-lived token naming the repository; it is checked against GitHub's keys, must match the repository you connected, and is accepted once. Pull requests from forks get no such token.
- The last 20 scans per connection are kept. Disconnecting a repository deletes it with every scan and finding.
- Each account's data is isolated in the database itself, with row-level security, not only in application code.

## The signed feed

The feed is the one thing the CLI and the Action download, and it decides what they report and what they may fix. It is trusted because its signature verifies, never because of the server it came from. Every download of `https://feed.harbyn.com/v1/feed.json` goes through these checks, in this order, before anything in it is used:

1. The signature envelope names a key id, and that id must be one pinned in the CLI's code.
2. The Ed25519 signature over the exact bytes must verify against that key.
3. The SHA-256 digest in the envelope must match the bytes.
4. Only then is it parsed, and it must match the feed schema.
5. It must not be past its expiry date, 4 days after it was generated.
6. It must not be older than a feed already accepted on that machine, so an old feed cannot be replayed.

If any check fails, nothing is reported and the CLI exits with code 2. A failed verification never falls back to a cached copy. The private signing key exists only as a secret of a protected CI environment that only the main branch can use; it is not in any repository. A second, recovery key is pinned too: its private half is kept offline and is used only to keep publishing while a leaked CI key is retired by a new release.

Production feed public key, Ed25519, id 78e488880f4fca98

```
-----BEGIN PUBLIC KEY-----
MCowBQYDK2VwAyEAae5XSWv2jl/tO6akP1MXv1FdrCpGhWa3x3D9y1aktsM=
-----END PUBLIC KEY-----
```

Offline recovery key, Ed25519, id d46fdcaf720b8e0b

```
-----BEGIN PUBLIC KEY-----
MCowBQYDK2VwAyEA2CA4i57YlJGoOGZ72AtMSCqGYvcs/w3R1PnPNxcXAzE=
-----END PUBLIC KEY-----
```

The key id is the first 16 hex characters of the SHA-256 of the DER-encoded public key. Every event in the feed also keeps its provenance: the source URL, when it was fetched, and the SHA-256 of the snapshot it was read from.

[Verify it yourself with OpenSSL or Node.js](https://harbyn.com/docs#verify)

## How model output is constrained

A language model helps read vendor changelogs. Vendor pages are treated as hostile input, and the model's answer is treated as untrusted too.

### Reading the web

- The step that reads vendor pages has no tools, no access to customer data, and no write access to Harbyn's own repository.
- The model must answer in JSON that matches a strict schema. Every identifier and date it reports must appear on the page it read, or the claim is dropped.
- Text fields are plain text, length-capped, without markup or control characters.
- Identifiers a model extracts are informational only: the scanner does not match on them until a review turns them into scan targets.
- High-impact events are held in a review queue and checked against the saved source before they are published. Each event records how it was extracted (parser, model or person) and whether it was reviewed.

### Separate stages

The side that touches code never sees raw web content. The CLI and the Action receive only signed, schema-validated events, and fixes are made only from reviewed events.

### Fixes proposed by a model, in your CI

This is optional and uses your own model key, called from your job. Harbyn never sees the key or the code. The model sees the change event and 15 lines around each finding, marked as data, with no tools. Its edits are checked in code against an allowlist before anything is written:

- it may only replace lines it was shown, never delete one: at most 8 lines per edit and 30 per change;
- every word must come from your code or the vendor's change; words that reach the network, start processes, read the environment or run code are allowed only where the replaced line already had them;
- no new imports; brackets, quotes and comment markers stay balanced; no new invisible characters;
- one failing edit drops the whole proposal; the result opens as a draft pull request that says a model wrote it.

## Limits on changes

Harbyn's output is always a pull request or a diff for a person to review. In code, not only in policy:

- Never a push to the default branch. Never a merge, an approval or a forced branch update.
- A pull request you close is never reopened. At most three fix pull requests per run.
- Payments, authentication and cryptography code is alert-only: reported, never edited. CI configuration is never edited either.
- Fix pull requests are built from files read from GitHub at the scanned commit, not from the runner's copy, so another step in your job cannot slip changes into the commit. The Action starts no processes.
- SDK migrations run in two jobs. The one that installs and runs dependency code has a read-only token, and the files to commit are sealed before your checks run the new versions. The job that opens the pull request runs nothing from your repository and verifies every sealed file against the scanned commit.
- A migration pull request is marked ready only when your own checks passed; otherwise it is a draft that says so.

## Supply chain

The CLI and the Action run inside your CI, so a bad release would be an attack on you. They are built to be checked.

- Open source under Apache-2.0, at [github.com/harbyn/cli](https://github.com/harbyn/cli)
- Releases are built by the repository's release workflow and carry signed build provenance for the tarball and the Action file. Verify with `gh attestation verify harbyn-<version>.tgz --repo harbyn/cli --signer-workflow harbyn/cli/.github/workflows/release.yml`.
- The package declares no dependencies and no install scripts. Everything is bundled and left unminified, so you can read what runs; bundled packages and their licenses are listed in `THIRD_PARTY_NOTICES.md`.
- The Action's bundle is committed so you can pin it by commit SHA, and CI fails if a fresh build differs from the committed file.
- Every build is tested by packing the release and installing it with scripts off. The scan CLI and the Action bundles must contain no way to start a process, no `eval` or `new Function`, no invisible or bidirectional characters, and no network address beyond the documented ones; for the scan CLI, that is the feed alone.
- The workflows in our docs pin every Action by full commit SHA, start from `permissions: {}`, and check out without persisting credentials.
- The CLI is published on npm by the release workflow of `harbyn/cli` with build provenance, and as signed GitHub releases whose attestation `gh attestation verify` checks.

## This website

- Static pages. No analytics, and no third-party fonts or images. One third-party script, Meta's Pixel, and only after you accept advertising cookies in the banner (ADR 0040); until then no Meta address is contacted, and Global Privacy Control counts as a reject. The dashboard, the API, the CLI and the Action never load it. What it and our server send is listed in the [privacy policy](https://harbyn.com/privacy#ads).
- Without that consent, this site sets one cookie of its own: your choice (harbyn\_consent). If you are signed in to the dashboard, your browser also sends it the dashboard's sign-in cookie, which is shared by our own subdomains; this site ignores it and does not log it. A small signed-in hint, with no content, lets the header show your account instead of "Log in".
- A strict Content Security Policy: only the site's own scripts and styles run, inline ones only by hash, plus Meta's Pixel from connect.facebook.net after consent; the page can connect only to our API and, after consent, to Meta's beacon at www.facebook.com.
- HTTPS only, with HSTS. Pages cannot be framed by other sites.
- Your light or dark theme choice is kept in your browser's local storage and never sent anywhere.

## Reporting a vulnerability

If you find a security problem in Harbyn, the CLI, the Action, the feed or this site, email [security@harbyn.com](mailto:security@harbyn.com). Reports in English or Portuguese are welcome. Please include the steps to reproduce it and what an attacker could do with it.

### What we commit to

- A reply from a person within 3 business days, and a first assessment within 10.
- Updates until it is fixed. Critical problems are fixed first, before anything else we are working on.
- Credit in the fix's notes, if you want it. We have no paid bounty yet.
- Coordinated disclosure: we publish what happened once it is fixed, and you may publish 90 days after your report, or earlier if we agree.

### Safe harbor

Research done in good faith under these rules is welcome, and we will not take legal action over it or ask anyone else to. Use your own accounts, or test accounts you create; never access, change or keep data that is not yours, and stop and tell us if you reach some.

### Out of scope

- Denial of service, load or volume testing, and spam.
- Social engineering of our team or our users, and physical attacks.
- Services we use but do not run (Cloudflare, GitHub, Stripe, Resend): report those to them.
- Findings without an impact, such as missing headers on pages with no data, or self-XSS.

[/.well-known/security.txt](https://harbyn.com/.well-known/security.txt)

## If something goes wrong

- If we confirm that someone reached data of yours, or that we published something that could harm your code, we email the address of every affected account within 72 hours: what happened, what it reached, what we did and what you should do.
- A signed feed or engine release we believe was tampered with is withdrawn, its key is retired, and the CLI and the Action refuse anything it signed.
- Afterwards we publish a report on this page: the cause, the timeline and what changed so it cannot repeat.
