OpenE2EE

CLI reference

The oe command. Installation, every command and flag, the exit codes, the environment variables, the JSON envelope, and agent mode.

oe is the OpenE2EE command-line interface. It creates and links Relay projects, and keeps open-e2ee.config.ts and the console in step. It also stages iOS notification profiles. oe help prints the commands, global flags, exit codes, and environment variables on this page. oe help --json returns them as data.

Install

oe needs Node.js 22.18 or later. Install it globally:

npm install --global @open-e2ee/oe

To run one command without an installation, use npx @open-e2ee/oe@latest in place of oe:

npx @open-e2ee/oe@latest auth login

Do not run npx oe. The npm package oe is not an OpenE2EE package.

@open-e2ee/oe is a small Node.js launcher. npm installs the native package for macOS, Linux, or Windows on arm64 or x64 as an optional dependency. Installation does not run a postinstall download.

Commands

Global flags can come before or after the command. oe COMMAND --help shows the usage of one command.

CommandWhat it does
oe new [signal-relay] [--project SLUG] [--name NAME] [--env-var NAME] [--dry-run]Creates a project and its Sandbox environment. Writes open-e2ee.config.ts, the Relay connection in .env.local, and the @open-e2ee/oe devDependency.
oe auth login [--accept-terms] [--organization ORG] [--timeout DURATION]Logs in with a browser and stores the session in the operating-system keychain. --accept-terms accepts the OpenE2EE terms for the organization. --organization puts the session in that organization. Without it, a person at a terminal who belongs to more than one organization chooses one. --timeout defaults to 5 minutes.
oe auth statusShows the person, the organization, the terms state, and where the session is. When the control API lists the organizations, the last line names each one with its role and marks the current one. Exits with code 4 without a session.
oe auth logoutRemoves the stored session.
oe org listLists the organizations of the account with the role of each, and marks the current one with *.
oe org switch [ORG]Moves the stored session to another organization. ORG is an ID, or a name that only one organization has. Without ORG, a person at a terminal chooses one.
oe doctor [--env sandbox|production] [--wait] [--timeout DURATION]Checks the config, session, project, and Relay connection of the selected environment. --wait then waits for the first acknowledged Sandbox message.
oe link [PROJECT] [--env-var NAME] [--yes] [--dry-run]Links the directory to a Relay project that exists, and writes the Relay connection of each active environment to its env file. Without PROJECT, it rewrites only the env files.
oe project listLists the Relay projects of the organization of the session. When the list is empty and the person belongs to another organization, next is oe org switch.
oe project show [PROJECT] [--env sandbox|production]Reads one project and the state of each environment.
oe project connection [PROJECT] [--env sandbox|production]Prints the Relay connection URL of one environment. Text mode prints only the URL.
oe config push [--env sandbox|production] [--yes] [--dry-run]Applies the Sandbox section of open-e2ee.config.ts, then the Production section when the file has one.
oe config pull [--env sandbox|production] [--yes] [--dry-run]Writes the Relay policy of each active environment into open-e2ee.config.ts.
oe notifications status [--env sandbox|production]Shows the allowed notification profiles and the providers of the environment.
oe notifications setup ios [--profile background-only|visible-alert] [--env sandbox|production]Stages an iOS notification profile. The default profile is background-only.
oe notifications add-nse [--env sandbox|production]Stages a generic Notification Service Extension.
oe notifications verify ios [--app-bundle PATH] [--env sandbox|production]Verifies the iOS notification setup. --app-bundle inspects a signed .app bundle.
oe notifications apple-filtering-requestGives the URL of the Apple request for the notification-filtering entitlement in data.requestUrl of the JSON output.
oe agent setup [--scope project|user] [--check]Installs the OpenE2EE skills for coding agents. --check reports each skill as current, missing, or changed, and writes nothing.
oe versionPrints the CLI version.
oe help [COMMAND]Shows all commands, or the usage of one command.

In oe config, a Production change or a replaced value needs --yes or the answer of a person at a terminal. --dry-run changes nothing.

Notifications

Push is a best-effort wake. The durable Relay mailbox, authenticated pull, and acknowledgment are delivery authority. Delivery and push describes the iOS workflow.

oe notifications status reads the notification settings of the environment. Its JSON data has allowedProfiles, environment, and providers.

oe notifications apple-filtering-request puts https://developer.apple.com/contact/request/notification-service/ in data.requestUrl. Run it with --json to see the URL, because text mode prints only a note. Apple's notification-filtering entitlement lets an approved, signed extension suppress an alert. It does not improve APNs delivery or execution. oe keeps filtering unavailable until Apple approval, signed extension inspection, and physical-device suppression evidence all pass.

Organizations

A person can belong to more than one organization. The session is in one organization at a time. Each project command reads only the projects of that organization.

oe org list
oe org switch "Beta LLC"
oe auth login --organization "Beta LLC"

In JSON, oe org list puts the ID of the current organization in data.current. Each entry of data.organizations has id, name, role, current, and switchable.

oe org switch matches a name in any letter case. Without ORG, a person at a terminal chooses from a list, and Enter keeps the current organization. After a switch, next is oe project list. An unknown or ambiguous ORG fails with USAGE_ERROR, and next is oe org list. For an unknown ORG, data.candidates lists every organization of the account. For an ambiguous ORG, it lists the organizations with that name.

oe auth login --organization ORG logs in, then switches to ORG when the session is in another organization. Without the flag, a person at a terminal who belongs to more than one organization chooses one after a new login.

Global flags

FlagEffect
--jsonWrites one JSON document to stdout for the result, for success and for failure. oe auth login writes one pending event before it.
--json-streamWrites newline-delimited JSON progress events, then one final event, to stdout.
--agent yes|no|autoSays whether a coding agent runs oe. The default, auto, reads the environment variables that coding agents set.
--env sandbox|production, -eSelects the environment.
--control-url URLUses another control API. The URL must use HTTPS, except on loopback.
-h, --helpShows help.

Without --env, oe doctor, oe project, and oe notifications read OE_ENV, then use sandbox. oe new always uses sandbox. oe config pull reads each active environment, and oe config push applies each environment section of the config. Only --env narrows a pull or a push to one environment. OE_ENV does not.

Environment variables

VariableEffect
OE_ACCESS_TOKENA scoped access token for CI or an agent. oe keeps it in memory and never stores it. When it is set, oe uses it in place of the stored login.
OE_ACCESS_TOKEN_SCOPESThe scopes of OE_ACCESS_TOKEN, separated by commas or spaces. When it is set, oe checks the scope of each command before the request.
OE_ENVThe environment of oe doctor, oe project, and oe notifications when --env is not given: sandbox or production. oe new and oe config ignore it.
OE_OPERATION_IDThe idempotency key for each remote mutation of one run. Set it only to retry one mutation.

An access token is short-lived. When OE_ACCESS_TOKEN has a value and oe exits with code 4, get a new token and set it in OE_ACCESS_TOKEN again. An agent that has no login gets its token through the auth.md registration and token exchange. oe auth login and oe auth logout do not change OE_ACCESS_TOKEN. To use the stored login, unset OE_ACCESS_TOKEN.

Exit codes

CodeMeaning
0The command succeeded.
1The command failed. code tells why.
2The command line is invalid, or a required input is missing, for example --yes.
4Authentication is required. Run next, which is oe auth login. After a failed switch, next adds --organization ORG. ACCESS_TOKEN_INVALID has no next. For ACCESS_TOKEN_INVALID, set OE_ACCESS_TOKEN to a new token, or unset it and run oe auth login.
5A person must act before the command can continue. error tells what to do. When action.url is present, it is the page that the person opens.
6The failure is temporary. next is the same command. Run it again later.

JSON output

In JSON mode, each run writes one JSON document to stdout. The document has these fields:

FieldValue
statusok, error, pending, or progress.
commandThe command, for example config push.
messageA summary for a person.
dataThe result of the command.
errorThe error text.
codeThe error code.
nextThe command that moves the task forward.
actionA step for a person: kind, url, and reason.
{"status":"ok","command":"project","message":"...","data":{}}
{"status":"error","command":"config push","error":"...","code":"CONFIG_NOT_FOUND","next":"oe new"}

Switch on code, not on the text of error. When action is present, give action.url to a person. In text mode, a failure goes to stderr as error:, action:, and next: lines, and stdout stays clean.

These are the error codes that a script usually handles:

CodeMeaning
AUTHENTICATION_REQUIREDNo session and no OE_ACCESS_TOKEN. Exit code 4.
SESSION_EXPIREDThe WorkOS session expired. Exit code 4. next is oe auth login. When the session expires during an organization switch, next is oe auth login --organization ORG.
ORGANIZATION_REQUIREDThe command needs an organization, and the session is in none. Exit code 1. next is oe org switch. With OE_ACCESS_TOKEN, there is no next.
ORGANIZATION_SIGN_IN_REQUIREDThe organization needs its own sign-in, for example SSO or MFA enrollment. Exit code 5. The stored session does not change. next is oe auth login --organization ORG. The person chooses that organization on the sign-in page.
ORGANIZATION_SWITCH_FAILEDThe control API confirmed another organization after a switch. Exit code 1. next is oe org list.
CONTROL_UPDATE_REQUIREDThe control API does not list organizations yet, so oe org and oe auth login --organization cannot run. Exit code 1.
TERMS_REQUIREDThe organization has not accepted the OpenE2EE terms. Exit code 5. data.retry is the command to run again after the acceptance.
TERMS_PERMISSION_REQUIREDOnly an administrator of the organization can accept the terms. Exit code 5.
ALREADY_SET_UPoe new ran in a directory that has open-e2ee.config.ts.
PROJECT_EXISTSThe organization already uses the project slug.
PROJECT_NOT_FOUNDNo project that this account can read has the slug in the organization of the session. Exit code 1. next is oe project list. When the account belongs to more than one organization, error says that the project can be in another one. In oe new, next is oe new with another project slug.
CONFIG_NOT_FOUNDThe directory has no open-e2ee.config.ts.
ENVIRONMENT_NOT_ACTIVEThe selected environment of the project is not active. For Sandbox, next is oe new. For Production, next is oe config push.
CONFIRMATION_REQUIREDA change needs --yes. Review the change, then run the command again with --yes.
BILLING_PERMISSION_REQUIRED, FREE_PROJECT_LIMIT, CARD_REQUIREDA Production activation needs the billing permission, an open Free plan slot, or a card on the organization. Exit code 5. For CARD_REQUIRED, action.url is the card page.
NODE_REQUIREDoe needs Node.js 22.18 or later. Exit code 5.
CONTROL_UNAVAILABLEThe control API is temporarily unavailable. Exit code 6.

Agent mode

oe detects a coding agent from the environment variables that the agent sets:

VariableAgent
OPENCODE=1OpenCode
QWEN_CODE_SESSION_IDQwen Code
PI_CODING_AGENT=true or AI_AGENT=piPi
CURSOR_AGENT=1Cursor Agent
CLAUDECODE=1Claude Code
CODEX_THREAD_IDOpenAI Codex
AMP_CURRENT_THREAD_IDAmp
GEMINI_CLI=1Gemini CLI
AUGMENT_AGENT=1Auggie
CRUSH=1 or AI_AGENT=crushCrush
COPILOT_AGENT=1 or AI_AGENT=github_copilot_vscode_agentGitHub Copilot in VS Code
OZ_RUN_IDWarp

A variable without a value in this table matches any value except an empty value, 0, false, no, or off, in any letter case. Under an agent, the default output is JSON, and oe never prompts and never opens a browser. Pass --agent yes for an agent that oe does not detect. --agent no restores text output.

Under an agent, oe auth login writes a pending event, then the result after the person approves. Start the command in the background. Give the person action.url and data.userCode. The person opens the URL and confirms that the page shows the code. On another device, the person goes to data.bareVerificationUrl and enters the code. data.expiresInSeconds is the time that the code stays valid.

Under an agent or --json, oe auth login does not ask for an organization. The session keeps its organization. When the control API lists the organizations, data.organizations has each one. To change the organization, run oe auth login --organization ORG or oe org switch ORG. Without ORG, oe org switch fails with USAGE_ERROR, and data.organizations lists the choices. An agent session belongs to one organization and cannot switch. oe org switch and --organization refuse a session from OE_ACCESS_TOKEN. These refusals exit with code 2.

oe agent setup installs the OpenE2EE skills for coding agents. It writes .agents/skills/<name> and links .claude/skills/<name> to it.

Relay connection variable

oe new and oe link write the Sandbox connection URL to .env.local. The variable name comes from the dependencies and devDependencies of package.json. The first row of this table that matches sets the name:

DependencyVariable
nextNEXT_PUBLIC_OPEN_E2EE_RELAY_URL
expoEXPO_PUBLIC_OPEN_E2EE_RELAY_URL
viteVITE_OPEN_E2EE_RELAY_URL
None of these, or no package.jsonOPEN_E2EE_RELAY_URL

--env-var NAME sets a different name. The URL is public configuration. It is not a credential. oe config push writes .env.production.local.

On this page