SecurityUpdated October 7, 2026
Where Arbor runs. What it can reach.
The website, the local engine and Arbor Cloud: what each can reach, the limits we set, and the gaps we know about.
Three places Arbor runs
What each one can reach and what it keeps. Each row links to the full section.
| Where | Status | What it can reach | What it keeps |
|---|---|---|---|
| The website | Live at getarbor.dev | Your browser, and the waitlist database when you join. AWS serves every page. | Waitlist emails and signup times, plus the referring page and utm_source tag when present. Public pages set no cookies and no visitor ID; signing in sets an encrypted session cookie. |
| The engine | v3.0.3, on your machine | Your files and your local git. It makes no network requests of its own. | .arbor/ in your project: the graph and, if you use receipts, your requests to Claude Code. Receipt snapshots go into your repository’s .git. |
| Arbor Cloud | Open since October 7, 2026 | One repository at a time, read-only, through Arbor’s GitHub App. | Reports with file paths, symbol names and counts. No source code. Payment records from Razorpay, never card numbers. |
Running today
The Cloud boundary, the public website and the engine on your machine.
Only the workbench is open
In shortTwo independent layers, the API and the website, serve the Cloud workbench and refuse every other Cloud route. Neither has a switch a visitor can flip.
Arbor Cloud reopened on October 7, 2026 as a new service. The first Cloud’s routes stay closed. Neither layer has a cookie, query-parameter or user-role switch that opens a closed route.
The API checks one setting before it loads credentials, a database, telemetry or workers. The workbench value mounts only health checks, the GitHub sign-in exchange, the account and workspace routes this website’s server calls, the GitHub App webhook and the Razorpay webhook. Every other path answers 404. A typo or different casing leaves it serving only /health.
The website serves a fixed list of public pages and files, plus sign-in, the workbench and the routes the workbench calls. The first Cloud’s admin and sharing pages and its API routes stay closed. A path that isn’t on the list returns 404.
| Request | Answer | Layer |
|---|---|---|
| GET /dashboard, signed out | 307 → /auth/signin | Website |
| GET /api/repos | 404 ROUTE_CLOSED | Website. The first Cloud’s API paths. |
| GET /health | 200 · mode: workbench_public | API |
| Any other API path | 404 ROUTE_CLOSED | API |
CI checks both layers. It builds the website closed and open and requests the routes that must stay closed, boots the API without credentials, and boots the production container.
About Arbor CloudThe public website
In shortStrict headers, nothing loaded from other hosts, and a waitlist rate limit that keeps only a salted hash of your IP address, in memory.
Every page carries a Content Security Policy that blocks framing, plugins and form posts to other sites. Responses also send Strict-Transport-Security for two years, X-Frame-Options: DENY, nosniff, and a Permissions-Policy that turns off the camera, microphone and geolocation. The two public API routes get default-src 'none' and are sent with no-store.
The policy still allows inline scripts, which the theme loader and the framework’s own page scripts use. Public pages load nothing from other hosts: fonts and images come from this site.
Each server instance allows 10 waitlist attempts a minute per client address. That stops bursts, not attempts spread across many instances or addresses. The limiter keeps only an HMAC-SHA256 of your IP address, keyed with a random salt that each instance creates in memory and never stores. It writes nothing to the database. Request bodies over 4,000 bytes are refused, and a hidden field turns away simple bots. The privacy notice lists what a sign-up stores.
The sample explorer uses an authored example bundled with the site. It does not read your files or connect to GitHub.
The engine runs on your machine
In shortNo telemetry, no update check and no network requests of its own. Data leaves your machine only through steps you take.
The Arbor CLI, MCP bridge and local servers include no HTTP client and make no network requests of their own: no telemetry, crash reports or update checks. Indexing, queries and receipts run locally. The engine runs git only for local commands such as diff, show and merge-base. It never runs fetch, pull or push.
Installing contacts the channel you choose: GitHub Releases, npm or crates.io. The npm package downloads its binary from GitHub Releases. Your MCP client receives the graph context its tools return and may send it to its model provider. The engine’s optional GitHub Action posts its report as a pull-request comment unless you set comment-on-pr: false.
.arbor/ holds the graph, with symbol names, file paths, signatures and docstrings, and, if you use receipts, the text of your requests. v3.0.3 does not add it to .gitignore, so add it yourself before you commit. The quickstart covers this.
Local ports
In shortThe bridge listens only on 127.0.0.1. arbor serve --headless is the exception: it listens on every network interface, with no authentication.
arbor bridge speaks MCP over stdio, and your MCP client starts it. It also opens local ports, and so do two other commands:
| Port | Opened by | Serves | Listens on |
|---|---|---|---|
| 7433 | arbor bridge, arbor viz | WebSocket graph queries | 127.0.0.1 |
| 8081 | arbor bridge, arbor viz | WebSocket graph updates for the visualizer | 127.0.0.1 |
| 3333 | arbor bridge --http | MCP over HTTP | 127.0.0.1 |
| 7432 | arbor serve | Graph queries | 127.0.0.1 |
| 7432 | arbor serve --headless | Graph queries | Every network interface, with no authentication |
The HTTP transport accepts only application/json and sends no CORS headers. Bodies are capped at 4 MiB, headers must arrive within 10 seconds and the body within 30, and it serves at most 64 connections at once. --port changes its port.
Use arbor serve --headless only on a network you trust: anyone who can reach its port can query the graph.
Dependencies and releases
In shortCI fails on known advisories and pins every GitHub Action to a commit. v3.0.3 ships without a checksums file or an attestation; the release workflow on main now adds both.
CI for the website and API runs on every pull request to master. It fails on any Rust vulnerability advisory that cargo audit finds in the API, with one documented exception in an optional MySQL driver the API never compiles, and on high or critical npm advisories in the website. It also produces a CycloneDX bill of materials for both, installs from lockfiles, and scans the repository and its history for live-looking credentials. Every GitHub Action in that CI is pinned to a full commit SHA.
The API image runs as an unprivileged user (UID 10001) that cannot modify its own binaries. CI builds the image as deployed, boots it with every Linux capability dropped, and checks the user, the closed boundary and a clean shutdown. An API release must name a successful CI run for the exact commit, and we then check that /health reports that commit.
The engine’s CI runs cargo-deny on pull requests. License and dependency-source checks fail the run. The advisory check reports without failing it until the open advisories are fixed.
v3.0.3 ships without a SHA256SUMS file or a provenance attestation, and the install scripts and npm wrapper don’t verify checksums. GitHub publishes a SHA-256 digest for each v3.0.3 asset, and the Scoop manifest pins the Windows archive’s digest. The engine’s release workflow on main now publishes SHA256SUMS and a signed provenance attestation for each archive, but no published release includes them yet.
v3.0.3 release assetsArbor Cloud
How the account-based Cloud handles access, tokens and analysis. Pull requests from forks and installations managed by an organization have had less testing than personal repositories; tell us if something looks wrong. The privacy notice and terms cover payments.
Access and tokens
In shortRead-only tokens for one repository at a time, rechecked with GitHub before every run and every report read. Repository tokens never reach the database.
Arbor reads repositories through its GitHub App. GitHub shows the App’s permissions when you install it. Each time Arbor needs a repository, it requests a token for that one repository, with read-only access to contents, metadata and pull requests. It rejects a token with any other permission or repository, or with 15 minutes or less left. Before a run, before a report is saved and on every report read, it asks GitHub whether you can still read that repository.
These repository tokens stay in memory and are never stored in the database. Git receives the token through an askpass helper and an environment variable, never in the clone URL, git config or command arguments. When you sign in, the token GitHub issues for your account is kept in your session cookie, which Auth.js encrypts and which expires 30 days after you last use Cloud. The API uses it to read your GitHub profile and doesn’t store it.
API logs pass through a writer that redacts known token formats, bearer values, private keys, passwords in URLs and secret query parameters. Error reports sent to Sentry are scrubbed the same way and drop request bodies, cookies, authorization headers, and stack-frame source and variables.
Requests from your account reach the API only through this website’s server, which proves itself with a shared secret of at least 32 characters, compared in constant time. Your GitHub identity comes from the server session, never from the request body, and a change sent from another site’s page is refused. Each API instance limits how many requests each signed-in user or client address can make a minute. GitHub webhooks need a valid HMAC-SHA256 signature, and a repeated delivery is ignored. A webhook only updates installation state. An analysis starts only from a signed-in request, never from a pull-request event.
The analysis worker
In shortA shallow checkout with no hooks, scripts or installs, analyzed in a separate process with limits on time, CPU, memory and open files. Resource limits, not a sandbox.
Each run checks out the exact pull-request head commit from github.com with a shallow, blob-filtered clone. Git hooks and submodules are off, and no build script or package install runs. Oversized checkouts are refused. Within the limits, source files beyond the number the worker analyzes, or larger than it parses, are skipped, and the report says its graph is partial.
To check dependencies, the run sends the names and versions of the crates.io and npm packages listed in Cargo.lock, package-lock.json and npm-shrinkwrap.json to the OSV vulnerability database at api.osv.dev. It skips packages the lockfile records as coming from git, a local path or another registry, and sends no source code. An npm entry that doesn’t record its source is sent, so a private package’s name can reach OSV.
Analysis runs in a separate analysis-worker process in its own Linux process group, with a cleared environment and no system or global git config. It has caps on wall time, CPU time, memory and open files, writes no core dumps and cannot gain new privileges. Its input and its report are size-limited. The API reads only a bounded amount of the worker’s error output and discards it, because engine diagnostics can include repository contents.
Reports keep file paths, symbol and branch names, commit IDs, dependency versions and advisories, the relationships between them, and risk labels. They don’t keep source text or patches.
These are resource limits, not a sandbox for hostile code. Today the worker runs inside the API container, not on separate machines. Each checkout sits in its own temporary directory, removed when the run ends. If the API process itself is killed, leftovers older than 20 minutes are deleted before the next run.
Claims and reports
What we don’t claim, and how to tell us about a problem.
What we don’t claim
We make no SOC 2, ISO 27001, uptime SLA or independent audit claims. CI and dependency alerts are review inputs, not proof that Arbor has no vulnerabilities.
Arbor’s analysis describes static relationships and their gaps. It never proves a change is safe.
Report a vulnerability
In shortEmail support@getarbor.dev with “Security report” in the subject. We coordinate disclosure with you; there is no paid bounty or guaranteed response time.
Include the affected URL, or the engine version and install channel, what you expected, what happened, a minimal reproduction and the impact.
A closed route that answers, a response that exposes data, or a build that bypasses the Cloud boundary is a security issue, even where old application code still exists.
Test locally or in an environment we have explicitly authorized. Please don’t submit test sign-ups to the live waitlist, send write requests to production, scan third-party infrastructure or try to reach other people’s data. Leave out access tokens, personal data and other secrets, and keep exploitable details out of public issues while we investigate.
We coordinate reproduction and disclosure with you in the reporting conversation. We don’t offer a paid bug bounty or a guaranteed response time.
Email a security report