Install
Four ways in. The bundle is the least fussy — it ships its own pinned Node runtime, so you don't need Node installed at all.
curl -fsSL https://privateer.pro/install.sh | sh
irm https://privateer.pro/install.ps1 | iex
npm install -g privateer-agent --ignore-scripts
npx privateer-agent
--ignore-scripts is the tested form and costs you nothing: the package deliberately declares no install scripts, and the patches it needs are applied at first launch instead. See verifying the install.
Platform notes
| Platform | Notes |
|---|---|
| macOS | Prebuilt bundles for Apple silicon and Intel. |
| Linux | x64 bundle. On arm64, use the npm or npx route. |
| Windows | x64 bundle; arm64 runs it under emulation. A bash is required — Git for Windows or WSL. The launcher checks at startup and tells you if it can't find one. |
If your bash lives somewhere unusual, point at it with shellPath in ~/.privateer/agent/settings.json.
First run
Start it in the directory you want to work in. The agent's world is that directory.
cd ~/code/my-project
privateer
Getting a model
You need one of three things. Pick whichever suits you — the agent behaves the same either way.
| Option | How | What it costs |
|---|---|---|
| Your Privateer account | /login | Billed to your plan & credit. Unlocks the confidential enclave models. |
| Your own provider key | /login keys, or set an env var before launching | You pay the provider directly. Privateer is not in the billing path. |
| A local model | Run Ollama and set OLLAMA_BASE_URL | Nothing. Inference stays on the machine. |
Provider keys are read from the environment: OPENROUTER_API_KEY, ANTHROPIC_API_KEY, OPENAI_API_KEY, GEMINI_API_KEY, XAI_API_KEY, GROQ_API_KEY, DEEPSEEK_API_KEY, NEAR_AI_API_KEY, TINFOIL_API_KEY, VENICE_API_KEY, OLLAMA_BASE_URL.
Signing in
/login uses the device-code flow. Privateer opens an Authorize this terminal? page on privateer.pro showing a short code, and you confirm it matches the one in your terminal.
Only ever approve a code you generated yourself. That prompt authorizes a terminal to act on your account. If a code arrives any other way — pasted to you, in an email, read out over a call — it is not yours, and approving it hands someone else a session. Nobody at Privateer will ever ask you to approve one.
On a machine with no browser, PRIVATEER_NO_BROWSER=1 prints the URL and code instead. /logout signs out this machine and revokes every terminal on it.
Teach it the project
/init writes a starter PRIVATEER.md in the current directory — project context the agent reads on every turn. Put the things a new colleague would need: what the project is, how to run it, the conventions you actually keep. AGENTS.md and CLAUDE.md are read too, so an existing file works as-is.
Models & privacy shields
/models opens a searchable picker. Beside every model is a shield saying what kind of privacy you're getting — and, crucially, how strong the evidence for it is. Those are two different things, and the picker refuses to blur them.
| Shield | Evidence | What it means |
|---|---|---|
| Verified TEE | cryptographic | Remote attestation ran, proved genuine enclave hardware, and the live TLS key matched the attestation report. |
| On-device | observable | A loopback endpoint — inference is local; no prompt leaves the machine. |
| ZDR (enforced) | observable | Routing is constrained to providers that contractually don't retain data, and the router refuses to serve when it can't meet the policy. Enforcement of a policy, not hardware attestation. |
| TEE (unconfirmed) | none | The provider claims an enclave, but attestation was incomplete or the live key couldn't be matched here. Treat as unverified until it goes green. |
| ZDR (by policy) | policy | The provider states it doesn't retain data. A promise we can't verify — not hardware, not attested. |
| Standard | none | No special privacy guarantee. |
A TEE claim starts yellow and only turns green once attestation actually succeeds, which happens in the background after you pick. /verify re-runs it on demand; /surface shows the tool surface and provenance for the current session.
The distinction is the point. "Verified" is reserved for routing this machine checked for itself — something we merely assert never earns it, no matter how confident we are. If a rung looks unflattering, that is the ladder working.
Terminal commands
What you can type at your shell, as opposed to inside a session.
| Command | What it does |
|---|---|
privateer | Start an interactive session in the current directory. |
privateer --version | Print the Privateer version and the Pi version underneath it. |
privateer --no-quarter | Start with the permission gate off. Prints a red banner. See unattended runs. |
privateer update | Update Privateer itself, using whichever installer you used. |
privateer update --extensions | Update every installed tool pack. |
privateer update <pack> | Update one pack, by npm name or git URL. |
privateer update --all | Tool packs first, then Privateer. |
privateer harbor | Run the Local Harbor in the foreground. |
privateer harbor install | Install it as a login service so it auto-starts and stays reachable with no terminal open. |
privateer harbor uninstall | Remove the login service. |
privateer harbor status | Show whether the service is installed and the Local Harbor is live. |
privateer verify | Check this installation: shape, provenance, dependency drift, patch state, where credentials live. --offline skips the registry checks. |
privateer acp | Speak the Agent Client Protocol over stdio, for editors. See editors & ACP. |
Slash commands
Typed inside a session. Tab-completion lists them; this is the whole set.
Account & session
| Command | Description |
|---|---|
/login | Log in to your Privateer account · /login keys for a provider API key |
/logout | Log out of Privateer on this machine (revokes all its terminals) |
/privateer | Privateer account: /privateer [status | login | logout] |
/models | Pick a model with its privacy posture (TEE / ZDR / standard) — searchable |
/verify | Re-check or fetch the attestation for the current model |
/surface | Show the session's tool surface and provenance |
/update | Update Privateer to the latest release, or /update [<pack> | check] for tool packs |
/signin and /signout still work as undocumented aliases — they were the original names and cost nothing to keep.
Permissions
| Command | Description |
|---|---|
/mode | Show or set the permission mode: default | acceptEdits | bypass | plan |
/no-quarter [on|off] | Lower the moat for this session — run unattended with no approval prompts |
Project & tools
| Command | Description |
|---|---|
/init | Create a starter PRIVATEER.md project-context file in this directory |
/connect | Add, enable, or remove MCP connectors (GitHub, Notion, Linear, …) |
/mcp | Show MCP server status — what actually connected |
/mcp-auth <server> | Authenticate with an MCP server over OAuth |
/extensions | Manage installed Pi extensions: /extensions [add <npm:pkg> | remove <npm:pkg>] |
/web-tools | Configure web search and fetch. Signed in, web_search and web_fetch already work on your account — no key to obtain. Use this to point them at a provider of your own instead (Brave, Tavily, a self-hosted SearXNG), which always takes precedence over the account route. |
/hints [on|off] | Rotating tips in the working line |
Voice
| Command | Description |
|---|---|
/speak | Spoken responses: [on | off | stop | test | provider [id] | voice [name] | rate [n] | length [n|full] | stream on|off | announce on|off] |
/talk | Voice input — records and transcribes into the composer: [provider [id] | send on|off | lang [code] | loop on|off | silence [ms] | vocab [words] | key [binding]] |
Sub-agents
Bounded parallel helpers, for work that splits cleanly: /run, /chain, /run-chain, /parallel, /prompt-workflow, /chain-prompts, plus the fleet controls /subagents-fleet, /subagents-models, /subagents-profiles, /subagent-cost and /subagents-doctor. A sub-agent inherits the parent's exact permission setup — it cannot quietly hold more than you do.
Remote
| Command | Description |
|---|---|
/remote-access on|off | Drive this terminal from the Privateer app. Off by default. |
Inherited from Pi
Privateer is a distribution of the open Pi agent, so Pi's own commands are all present: /settings, /export, /import, /share, /copy, /name, /session, /new, /resume, /fork, /clone, /tree, /compact, /reload, /trust, /hotkeys, /changelog, /scoped-models.
/workflow is deliberately not in the terminal. Multi-step declarative workflows are authored in the app and executed by the Local Harbor, because a workflow that outlives your terminal session is the only kind worth having. /model redirects to /models so there is one picker, not two.
Keyboard
| Key | Does |
|---|---|
| shift+tab | Toggle no quarter — the permission gate off and on. |
| alt+t | Push-to-talk (with voice set up). |
| ctrl+t | Hide thinking text. Make it stick in /settings. |
| alt+enter | Queue a follow-up without interrupting the current turn. |
| ctrl+o | Expand collapsed tool output. |
| ctrl+g | Draft a long prompt in $EDITOR. |
| @ | Reference a file in your prompt. Tab completes the path. |
Rebind anything in ~/.privateer/agent/keybindings.json; /hotkeys shows the live map.
Mentioning files with @
Typing @src/server.ts inlines that file for the turn — text is appended to your message, images are attached for vision. Two limits worth knowing: a text file is read up to 256 KB and an image up to 5 MB, and mentions are confined to the working directory. @../secrets.env is skipped rather than prompted for, because a mention bypasses the approval gate by design and a bypass must not be able to reach outside the project.
Files & settings
Everything Privateer keeps lives under one directory, so there is a single tree to inspect, back up, or delete.
~/.privateer/
├── credentials.json # account tokens (0600)
├── config.json # Privateer prefs: remote, webhooks, posture, hints, channels, acp
├── account-sessions.json # per-terminal inference sessions (0600)
├── speak.json # voice settings
├── routines/ # routine output, dated, plus latest.md
├── workflows/ # one file per saved workflow (0600)
├── bin/privateer # the command shim
└── agent/ # the Pi agent dir
├── auth.json # provider keys
├── models.json settings.json trust.json keybindings.json
├── mcp-desktop.json # connectors — the source of truth
├── mcp.json # generated projection: enabled only. Don't hand-edit.
├── mcp-oauth/ # connector OAuth tokens
├── sessions/ # your history, as files on your own disk
└── extensions/ # drop your own Pi extensions here
Your conversation history stays here, on your machine. Nothing about a session is uploaded unless you turn on remote access or use an account model, which sends that turn's prompt for inference the same way any provider call does.
Per-project configuration
A project directory can carry its own settings, skills, prompts and extensions. Privateer reads .privateer/ and .pi/, in that order — and it only ever writes to .privateer/, so a repo that already uses Pi keeps working untouched.
my-project/
├── PRIVATEER.md # project context (or AGENTS.md / CLAUDE.md)
└── .privateer/
├── settings.json # deep-merged over the global one; arrays replace
├── skills/ # project-specific skills
├── prompts/ themes/ extensions/
├── SYSTEM.md # replaces the system prompt
└── APPEND_SYSTEM.md # adds to it
Where both exist and disagree, .privateer/ wins. Launch with --no-context-files (or -nc) to ignore context files entirely for one run.
Environment variables
| Variable | Effect |
|---|---|
PRIVATEER_HOME | Move the whole config tree somewhere else. |
PRIVATEER_MODEL | Start on a specific model. |
PRIVATEER_MODE | Start in a specific permission mode. |
PRIVATEER_NO_QUARTER | Start with the gate off. Same as --no-quarter. |
PRIVATEER_NO_BROWSER | Print the sign-in code instead of opening a browser. |
PRIVATEER_NO_SPLASH | Skip the startup splash. |
PRIVATEER_API_KEY | Use a Privateer API key instead of an interactive login. |
PRIVATEER_SERVER_URL | Point at a different Privateer server. |
PRIVATEER_DEBUG | Verbose diagnostics. PRIVATEER_RELAY_DEBUG for the relay specifically. |
PRIVATEER_FFMPEG, PRIVATEER_FFPROBE | Point the media tools at specific binaries. |
HARBOR_WEB, HARBOR_MEDIA | Turn web access and media generation on or off for the Local Harbor. |
PRIVATEER_WEB_SEARCH | privateer to search on your account even when you have a provider key configured; own to insist on your own provider. |
The moat — how permissions work
Every action the agent can take is classified before it happens: shell commands, file writes, tool calls, anything an MCP connector offers. An action that clears the current mode runs; anything else stops and asks you.
| Mode | Behaviour |
|---|---|
default | Ask before anything that writes, runs, or reaches outside the project. |
acceptEdits | File edits inside the project go through without asking. Shell and everything else still ask. |
bypass | Far fewer prompts, for when you're watching closely. |
plan | Read and reason only. Nothing is changed or run. |
Switch with /mode.
Two filters sit above every mode, and no mode relaxes them: a danger classifier that refuses destructive shell commands, and a protected-path list that guards files you would not want rewritten by accident. Relaxing the mode widens what runs without asking — it does not widen what is allowed to run.
Tools that come from a connector are gated identically to built-ins. A tool is not trusted because you configured the server it came from.
Unattended runs — “no quarter”
Sometimes you want the agent to just go: a long migration, a batch job, something you'll review at the end. shift+tab, /no-quarter on, or launching with --no-quarter turns the approval prompts off for the session.
While it's on, the banner is red and a footer indicator stays lit — there is no way to be in this state and not know it. Sub-agents inherit it.
Shell commands, edits, out-of-directory access and protected files all run without asking. The danger filter still holds — destructive shell commands stay blocked even here — but treat the working directory as fully writable and think about what's reachable from it before you start. No quarter is also deliberately unreachable from the phone: it is a decision you make at the machine.
Connectors (MCP)
A connector is a Model Context Protocol server whose tools become the agent's tools. /connect opens a picker over a curated catalog — GitHub, Slack, Notion, Linear, Jira & Confluence, Sentry, Stripe, Asana, Supabase, Figma, Gmail, Google Calendar, Drive and Docs, PostgreSQL, Playwright, Brave Search, Google Maps, Filesystem and more — plus a Custom connector entry that takes any stdio command line or https:// URL.
New tools are live in the session you're already in; the adapter reloads in place. /mcp shows what actually connected, which is the thing to check first when a tool doesn't appear.
| Transport | What it means |
|---|---|
stdio | Local. The server runs as a child process on your machine. Nothing leaves the box except what that server itself chooses to send. |
http | Remote. An endpoint someone else hosts, authenticating with OAuth (/mcp-auth <server>), a bearer token, or nothing. Whatever the agent hands it leaves your machine. |
Configuration lives in two files under ~/.privateer/agent/: mcp-desktop.json is the source of truth and holds every connector with an enabled flag; mcp.json is generated from it and holds only the enabled ones. Edit the first, never the second. Because both sit in the shared home, the terminal, the desktop app and the phone all see one coherent list.
Connector secrets are stored in plaintext — environment values, bearer tokens, an Authorization header — in mcp-desktop.json on that machine. This is unavoidable: the adapter has to hand the real token to the server. Protect the file the way you protect ~/.aws/credentials. The masked input in /connect is screen-share hygiene, not a storage claim.
Editing connectors from the phone works differently, because the relay in between is untrusted: secrets are write-only in both directions. A listing returns names and which are set, never values; a value typed on the phone is sealed to that terminal's key before it leaves the device.
The Local Harbor
The Local Harbor is a resident agent on your own computer. It runs scheduled routines and chat bridges, comes back at login, and outlives the terminal that started it — which is what makes scheduled work actually work.
privateer harbor install # auto-start at login
privateer harbor status # installed? live?
privateer harbor # or just run it in the foreground
| Platform | How it installs |
|---|---|
| macOS | A launchd user agent. No root, no admin prompt. |
| Linux | A systemd --user unit. No root. |
| Windows | No login service. Run it in a terminal, or let the desktop app supervise it. |
"Local Harbor" and "Harbor" are different things. The Local Harbor is this — a process on your own machine, under your own account, with your files. Harbor on its own means Privateer's hosted agents, which run on our infrastructure inside a hardware enclave we can't read into. Different machine, different promise. This guide only covers the local one.
A Local Harbor is headless by design. Drive it from the app by attaching to it — it has no terminal to take over, and it never speaks aloud.
Routines — scheduled work
A routine is a prompt on a schedule, run by the Local Harbor. Ask the agent to create one in plain language, or write it directly. Schedules are either a 5-field cron expression or a one-off ISO timestamp.
Results are delivered wherever you say:
| Channel | Where it lands |
|---|---|
file | Default. A dated directory under ~/.privateer/routines/, plus latest.md. |
notice | A notification on the machine. |
relay | Straight to an attached app session. |
cloud | The encrypted outbox — shows up in the app's Inbox on any of your devices. |
email | Emailed to you. |
webhook:<name> | POSTed to a webhook you configured in ~/.privateer/config.json. |
A routine runs with a safe read and web toolset by default; anything beyond that is an explicit grant, and extra grants are flagged when you approve the routine. A routine can also carry a per-connector allow-list, so an unattended job can hold exactly GitHub's create_issue without holding all of MCP.
Voice
Two independent halves. /speak on reads answers aloud; /talk types what you say into the composer, with alt+t as push-to-talk.
By default speech uses your operating system's own voice — say on macOS, espeak-ng on Linux, System.Speech on Windows — so nothing leaves the machine. Signed in, it upgrades to the account's confidential speech models. Voice input needs a capture tool on PATH: sox, or arecord, parecord or ffmpeg.
Settings persist in ~/.privateer/speak.json. Voice is off by default and interactive-only: the Local Harbor, ACP sessions and chat bridges never speak.
Two known gaps on the account voice provider: /speak rate and /talk vocab aren't supported there. Both work on the OS voices.
Editors & the Agent Client Protocol
Privateer speaks ACP, the interface Zed and other editors use to talk to coding agents. The host launches privateer acp and speaks JSON-RPC over stdio: the host owns the transport and the interface, Privateer owns the agent.
The moat comes with it. The host renders approvals; it does not grant them. A cancelled dialog, an unparseable answer, or a host that can't be reached all resolve to deny. Set the ceiling yourself in ~/.privateer/config.json — a host cannot widen it:
{
"acp": {
"tools": ["read", "grep", "find", "ls"],
"posture": "approve"
}
}
The default is that read-only ceiling. posture is readonly, approve (each risky action asks) or auto. The working directory comes from the host per session, and anything outside it is refused rather than prompted for.
Some hosts auto-approve. Buzz, for instance, answers every permission request itself by choosing "allow once" — there is no path to a human and no setting that changes it. Privateer still classifies every action and still refuses anything outside the working directory, but under such a host approve behaves like auto. Your real controls are acp.tools and acp.posture: choose the ceiling assuming everything in it will run unattended.
Setup for Zed, and the full Buzz walkthrough, are in the API reference's integration section.
Drive it from the app
/remote-access on lets the Privateer app on your phone, tablet or another computer drive this terminal. It is off by default and you approve the terminal in the app before anything connects.
From there the app can watch the stream, send prompts, answer approval prompts, switch the model, and manage connectors, skills, routines and extensions on that terminal. What it cannot do is turn no quarter on — that decision stays at the machine.
Multiple terminals on one computer group under that machine, so a phone shows you which box each agent is on.
Updating
privateer update knows how you installed it and does the right thing — re-running the bundle installer, or npm i -g privateer-agent@latest. There's a passive daily check that tells you when a release is out; it never installs anything on its own.
Tool packs update separately, so you can take a connector fix without moving the agent: /update check to see what's available, privateer update --extensions for all of them, privateer update --all for packs and then the CLI.
Uninstalling
One command, and it undoes what the installer did:
curl -fsSL https://privateer.pro/uninstall.sh | sh
It removes the bundle in ~/.privateer/app, the privateer launcher, the PATH line the installer added to your shell profile, and — first, before any of the files it runs on — the Local Harbor login service, so nothing is left running after the files it needs are gone.
Your sign-in stays unless you ask for it. ~/.privateer holds your device login, model list and settings, and the desktop app shares it, so the script keeps it and tells you how to remove it. Pass --all to take it in the same run, or --dry-run to see exactly what would go without changing anything. Remember there is no password recovery: once that sign-in is gone, anything still encrypted with it is gone with it.
Installed through npm instead? npm uninstall -g privateer-agent removes the command; the uninstaller above still handles the login service and ~/.privateer.
The desktop app is separate software and removes itself: Settings → Uninstall Privateer inside the app sweeps its own bundle, its app data, its copy of the privateer shim and the login service, and asks before touching ~/.privateer. On Windows, Add/Remove Programs does the same job. Dragging the app to the Trash leaves its data — and, if you turned one on, its login service — behind.
Verifying the install
An agent with shell access is worth being careful about, so the install is built to be checkable.
privateer verify # install shape, provenance, dependency drift, patch state
privateer verify --offline # skip the registry checks
npm audit signatures
npm view privateer-agent dist.attestations
The package declares no install scripts at all — nothing runs at npm install time. The patches Privateer needs over its Pi base are applied at first launch instead, where you can see them. Publishing happens from CI with provenance attestations, never from a laptop.
The CLI is open source under MIT: github.com/privateer-agent/privateer-agent. The Privateer app server is a separate, closed codebase — what we publish for it is a transparency layer you can inspect, not the whole server.
Troubleshooting
Windows: it exits saying it needs a shell
Privateer needs a bash. Install Git for Windows or use WSL. If yours is installed somewhere unusual, set shellPath in ~/.privateer/agent/settings.json.
privateer isn't found after installing
The bundle installers put the shim in ~/.privateer/bin. Make sure that's on your PATH, then open a new terminal. If you installed through npm, check npm bin -g is on the path. The desktop app can install the shim for you from its Agents screen.
Sign-in doesn't open a browser
Set PRIVATEER_NO_BROWSER=1 and Privateer prints the URL and code for you to open elsewhere. Over SSH this is usually what you want.
“Your Privateer session expired”
Run /login again. If it keeps happening, /logout then /login clears the machine's credentials and starts fresh — note that this revokes every terminal on the machine.
A connector's tools don't show up
/mcp first — it shows what connected and what failed. For an http connector needing OAuth, run /mcp-auth <server>. For a stdio one, check the command actually runs in your shell; a version-managed node that isn't on the minimal PATH is the usual culprit.
Something worked yesterday and doesn't today
privateer verify reports dependency drift and patch state, which catches most of these. PRIVATEER_DEBUG=1 for detail.
A scheduled routine never ran
privateer harbor status — a routine only runs while the Local Harbor is up. On Windows there is no login service, so it runs only while a terminal or the desktop app is supervising it.
Still stuck
privateer.pro/support, or open an issue on GitHub.