Security Model
Credential vault, sandbox isolation, OAuth trust boundaries.
Harbor's job is to hold credentials so your agents don't have to. This page documents the trust boundaries, the encryption story, and what you should never put where.
Trust boundaries
| Boundary | What can cross | What can't |
|---|---|---|
| Agent ↔ Harbor (MCP) | MCP frames over HTTPS with an OAuth bearer token | Plugin credentials. The agent never sees Linear / GitHub / Slack tokens. |
| Harbor ↔ Plugin | One decrypted credential, for one upstream call, briefly | Persistent token reuse outside the invoker scope. |
| Isolate ↔ Workspace | plugins.*, orbit.*, sand.* bindings | Raw filesystem, process env, other workspaces, arbitrary fetch() |
sand.* ↔ User machine | Curated CLI namespaces (git, gh, wrangler, …) | General shell access, arbitrary local binaries |
| Workspace ↔ Workspace | Nothing | Everything. Each workspace is fully isolated in D1, KV, R2. |
Credential vault
OAuth tokens, API keys, service-account JSONs are stored encrypted in the workspace credential vault.
- Cloud: AES-GCM with a per-workspace data key, derived from a master key in Cloudflare Secrets Manager. The decrypted value lives in memory for the lifetime of one plugin invoke and is zeroized after.
- OSS: AES-GCM at rest in
.harbor/credentials.enc, keyed by theHARBOR_LOCAL_CREDENTIAL_KEYenv var on the host. The key never hits disk. If you rotate it, existing credentials become unreadable and need to be re-issued.
Open source self-hosters: HARBOR_LOCAL_CREDENTIAL_KEY is the entire
auth boundary. Anyone with that env var on the machine has the vault.
Treat it like an SSH private key.
OAuth flow
- User clicks "Connect" in the dashboard or starts OAuth through an SDK/admin flow
- Harbor redirects to the upstream OAuth provider (Linear, GitHub, …)
- User consents in their own browser
- Provider redirects back to Harbor's loopback callback (OSS) or hosted callback (Cloud)
- Harbor exchanges the code for tokens
- Tokens encrypted into the vault, plugin source flips to
ready
The agent never participates in the OAuth flow. It can ask Harbor to
start the flow (hrbr.auth.start()) and Harbor opens the user's
browser, but the consent + the token issuance happen between the user
and the upstream provider.
Sandboxed execution
hrbr exec and every Function handler run inside a cloud isolate.
The isolate has:
plugins.*— namespaces typed by tool schemasorbit.*— workspace primitives (storage / cache / db / ai / tools / socket)sand.*— bridges to the original caller's machine via the local hrbr daemon (Cloud + caller has the local CLI installed and authenticated)step.*— durable-execution atoms
The isolate does not have:
fetch()to arbitrary URLs (only the plugin invoker can call upstreams)- Filesystem access
- Process env vars
- Reflection into Harbor internals
- Other workspaces' state
Output is capped at 1 MB per run. Memory at 128 MB per isolate. Wall time 30 s for sync exec, plan-dependent for Workflows.
What never to store where
| Storage | Encrypted? | Workspace-readable? | OK to put |
|---|---|---|---|
| Credential vault | Yes (AES-GCM) | No (vault is server-only) | Tokens, keys, secrets |
orbit.storage | Encryption at rest (R2 default-managed key) | Yes | Reports, artifacts, public reads |
orbit.cache | Encryption at rest (KV default-managed key) | Yes | Short-lived non-secret state |
orbit.db | Encryption at rest (D1 default-managed key) | Yes | Structured non-secret records |
| Run input/output | Encryption at rest | Yes (workspace members) | Inputs + return values without secrets |
Don't put OAuth tokens, API keys, or PII into orbit.storage /
orbit.cache / orbit.db. They are workspace-readable. The
meta-skill harbor-orbit
encodes this rule.
Audit trail
Every workspace action is captured in the activity log:
- Plugin install / connect / disconnect / refresh
- Member added / removed / role-changed
- Function / App / Skill published or disabled
- Run started, completed, failed, cancelled
The activity log is workspace-readable but immutable from the dashboard
UI. Owners can export the log via /audit API or the dashboard.
Data retention
| Class | Cloud retention | OSS |
|---|---|---|
| Trace + spans | Plan-dependent (7d Hobby → 1y Enterprise) | Until you delete .harbor/ |
Artifacts (orbit.storage) | Plan-dependent | Until you delete |
Cache (orbit.cache) | TTL per key | TTL per key |
| Audit log | 1 year | Until you delete |
| Vault credentials | Until you disconnect the plugin | Until you remove the source |
Reporting a security issue
Open a private security advisory at github.com/zonko-ai/harbor/security/advisories.