README
¶
Kei CLI
The Kei CLI manages Kei bot installation metadata. It authenticates operators, creates an installation for a bot platform, assigns agents, and reports the installation status. Runtime deployment and credentials are managed outside this CLI.
Install
Binary releases of kei-cli are available for macOS and Linux (arm64 and amd64). The installer downloads the matching archive and verifies its SHA-256 checksum.
Option 1: curl install (recommended)
The install script is hosted at https://haikeilabs.com/install.sh. If that URL is unreachable,
fall back to https://kei-cli-releases.s3.us-east-1.amazonaws.com/kei-cli/install.sh instead.
AWS_S3_RELEASES_URL_BASE can override the S3 endpoint. The install script detects your OS and
architecture and defaults to /usr/local/bin:
# Install the latest version to a user-writable directory:
export PATH="$HOME/.local/bin:$PATH"
curl -fsSL "https://haikeilabs.com/install.sh" \
| sh -s -- -d "$HOME/.local/bin"
# Install a specific version:
curl -fsSL "https://haikeilabs.com/install.sh" \
| sh -s -- -v 0.2.0 -d "$HOME/.local/bin"
After installing, verify:
kei help
kei --version
Managing the installation
The installer records what it installs in a .kei-install-manifest file in the
install directory. Use it to install only the proxy or to remove the
installation:
# Install only kei-proxy (from the standalone kei-proxy release). The default
# version is the pinned KEI_PROXY_VERSION; override it with -v.
curl -fsSL "https://haikeilabs.com/install.sh" \
| sh -s -- --proxy-only -d "$HOME/.local/bin"
# Remove exactly the files the installer recorded (kei, kei-proxy, and the
# manifest) from the install directory.
curl -fsSL "https://haikeilabs.com/install.sh" \
| sh -s -- --uninstall -d "$HOME/.local/bin"
Option 2: go install (development use)
For development environments with Go 1.27+, you can build and install from the Go module:
go install github.com/HaikeiLabs/kei-cli/cmd/kei@latest
export PATH="$(go env GOPATH)/bin:$PATH"
Add the export line to ~/.zshrc (or your shell's startup file) to make it
permanent. For deployed binaries, use the S3 release installer above.
Upgrade a deployed CLI in place from the official S3 release endpoint (no Go installation required):
kei upgrade # latest stable release
kei upgrade --version 0.2.0 # pin a release (optional leading v accepted)
Upgrade independently resolves the latest published kei and kei-proxy releases,
downloads matching macOS/Linux and amd64/arm64 archives, verifies their SHA-256
checksums, then replaces kei and the sibling kei-proxy. --version pins only
the kei CLI; the proxy is still upgraded to its latest published version. Both
artifacts are fetched and verified before either binary is replaced. The
current executable's directory must be writable. To use a mirror or alternate
endpoint, set AWS_S3_RELEASES_URL_BASE; otherwise https://kei-cli-releases.s3.us-east-1.amazonaws.com
is used. GPG signature verification is not performed by this command; checksum
verification follows the official installer's required SHA-256 check.
Building from source
go build -o tmp/kei ./cmd/kei/
./tmp/kei help
Configure a local runtime
To run a local headless harness against an existing Kei runtime installation, run setup once:
kei setup
Setup prompts for the production control-plane URL, runtime installation token,
local harness URL, kei-proxy path, and optional model settings. It verifies the
token with Kei before saving the configuration to ~/.config/kei.yaml. The file
is created with 0600 permissions and the token is never printed. For scripted
or non-interactive setup, pass the token explicitly (be aware it may be saved
in shell history):
kei setup \
--control-plane-url https://runtime.haikeilabs.com \
--runtime-token RUNTIME_INSTALLATION_TOKEN
Verify the saved runtime installation and send its heartbeat through the local
kei-proxy binary:
kei runtime bootstrap
Use --config PATH if you need a separate configuration file. The runtime
token is used only by the local proxy; it is not sent to the model or written
to the repository.
Runtime service
The runtime can run as a background service that heartbeats independently of
any harness, using the runtime token in ~/.config/kei.yaml (not the kei
login token):
kei runtime service install
kei runtime service status
kei runtime service uninstall
kei runtime service install writes a LaunchAgent on macOS
(~/Library/LaunchAgents/com.haikeilabs.kei-runtime.plist) or a systemd
--user unit on Linux (~/.config/systemd/user/kei.service), then loads
and starts it. The service runs kei runtime heartbeat --config <path> on a
loop and restarts automatically if it exits. On macOS, logs go to
~/Library/Logs/kei-runtime.log.
kei runtime service status reports whether the service is installed and
running. An installation that has never sent a heartbeat reports as
pending in kei bot status until the first successful heartbeat arrives.
kei runtime service uninstall stops the service and removes the definition
file. No --yes flag is needed.
Login
The CLI uses Kei device authorization. It prints a browser URL and one-time verification code; complete the flow in your browser:
kei login
The approval page opens automatically. Use --no-browser in a headless
environment.
The login token is stored in the operating system credential store. It is not printed or written to the repository.
Logout
Remove the stored CLI token for a Kei environment:
kei logout
Logout only removes the local credential from the OS keychain; it does not revoke the token server-side. It is idempotent: running it while not logged in succeeds and reports that no session was stored.
Feedback
Submit feedback to the Kei support channel with optional supporting files:
kei feedback \
--description "The session stopped unexpectedly." \
--session ./session.jsonl \
--screenshot ./error.png
Repeat --file, --session, or --screenshot to attach more evidence. The CLI
shows the description and file names/sizes, then asks before submission; use
--yes to skip the confirmation. Submissions require kei login and are sent
to the same bug-report endpoint as Console reports. Requests are limited to
4 MiB total and eight evidence files. Text attachments are redacted for common
secrets and email addresses by the server. Review images and other binary
attachments yourself before confirming; their contents are not redacted.
--export attaches a redacted transcript from a coding harness:
kei feedback --export claude [--session ID] (without --session the latest
session for the current directory is used), and likewise for codex and
opencode. Claude Code sessions run from the Claude Desktop app already work
with --export claude (the same ~/.claude/projects files). Plain Claude
Desktop chats (claude.ai) use --export claude-desktop --file EXPORT, where
EXPORT is the data export .zip or its conversations.json; pick one
conversation with --session UUID_OR_NAME, or re-run without --session to
list up to 20 conversations. Exported transcripts go through the same
redaction, preview and confirmation as any other transcript and are never
uploaded unredacted.
Manage installations
List runtime installations for an organization (the CLI does not persist an org ID, so --org is required):
kei bot list --org ORGANIZATION_ID
kei bot list --org ORGANIZATION_ID --all
kei bot list --org ORGANIZATION_ID --json
The table includes installation ID, name, platform, status, workspace IDs, and relative last-heartbeat time. --all fetches all pages; otherwise the command fetches one page and indicates when more results are available. JSON output contains the safe installation items only.
Create pending installation metadata for a bot:
kei bot init \
--platform cli \
--name "Local OpenWebUI" \
--workspace "My Workspace"
Use --platform cli for a platform-neutral local runtime such as
kei-connector-runtime behind OpenWebUI. The existing teams, discord, and
slack values remain for chat-platform installations. The --workspace flag
is optional on init; when provided, the workspace ID or name is forwarded to
the installation record.
The command prints the installation ID. Use that ID to inspect the installation or manage its agent assignments:
kei bot status --installation INSTALLATION_ID
kei bot update --installation INSTALLATION_ID --name "Renamed runtime"
kei bot delete --installation INSTALLATION_ID --yes
kei bot agents list --installation INSTALLATION_ID
kei bot agents add --installation INSTALLATION_ID --agent AGENT_ID
kei bot agents add --installation INSTALLATION_ID --agent AGENT_ID --default
kei bot agents remove --installation INSTALLATION_ID --agent AGENT_ID
kei bot status includes the installation lifecycle fields above and, when the
server has reported it, a read-only policy_bundle snapshot with its state,
version, revision, digest, timestamps, and safe reason code. A null value means
no compatible health report is available; it does not change installation
status or binding_status. The CLI never prints bundle contents or runtime
credentials. Health projection availability depends on the runtime/catalog
producer rollout.
To create or rotate a runtime credential for a command pipeline, stdout must be redirected or piped; the CLI refuses to print a credential to an interactive terminal:
kei bot credential --installation INSTALLATION_ID --workspace WORKSPACE_ID | secret-manager import
kei bot credential --installation INSTALLATION_ID --workspace WORKSPACE_ID --rotate | secret-manager import
The --workspace flag is required and accepts a UUID or an exact workspace
name. When a name is given, the CLI resolves it to a UUID by calling the
workspaces API. The workspace value can also be set via the KEI_WORKSPACE_ID
environment variable.
List available workspaces:
kei workspaces list
kei workspaces list --json
Create a workspace (organization admins). The server records you as its creator and grants you initial membership:
kei workspaces create --name e2e --yes
Create a group in one workspace. --workspace is required, so a group never
lands in the default workspace by omission:
kei groups create --name e2e-analysts --workspace e2e --description "connector e2e" --yes
List a workspace's groups, for example to find a group's ID by name:
kei groups list --workspace e2e --json
The CLI always targets the Kei production API at https://app.haikeilabs.com.
Set the KEI_WEB_URL environment variable to override the endpoint for
development and testing.
Manage connectors
Connector setup renders from the kei.connector-setup/v1 schema in
kei-connector-contracts v0.3.0, the same definition the console uses. The
CLI offers the providers Kei has built connectors for: gmail,
google_drive, linear, github, tito, skills_repo, crm, and the
generic http_api.
# OAuth: pick the account model (per_user is the default).
kei connectors create --workspace Main --provider gmail --name team-mail
kei connectors create --workspace Main --provider google_drive --name shared-drive --account-model shared
kei connectors reconnect CONNECTOR_ID --workspace Main # opens the consent page for a shared account
# Shared secret: non-secret fields with --set; the secret is read without echo.
kei connectors create --workspace Main --provider tito --name events --set account_slug=acme
<secret-manager read> | kei connectors create --workspace Main --provider tito --name events --set account_slug=acme
kei connectors reconnect CONNECTOR_ID --workspace Main # replaces the secret (a new generation)
# Skills repository (skills_repo): the workspace's agent skill source on GitHub.
# ref defaults to main and subdirectory to skills/ ("/" is the repository root).
# Users read it with their own GitHub OAuth (per_user), or pass --credential-ref
# for a shared token already in your secret manager.
kei connectors create --workspace Main --provider skills_repo --name org-skills \
--set repo_url=https://github.com/HaikeiLabs/skills [--set ref=v1.2.0] [--set subdirectory=skills/]
kei connectors create --workspace Main --provider skills_repo --name org-skills \
--set repo_url=https://github.com/HaikeiLabs/skills --credential-ref kei/prod/skills/github-token
# Generic HTTP API (http_api): name it, register its https base URL and the
# methods and paths agents may call. The shared API key is read without echo,
# sealed, and stored under a kei:// reference the catalog assigns.
<secret-manager read> | kei connectors create --workspace Main --provider http_api --name crm \
--base-url https://crm.example.com --methods GET,HEAD,POST,PATCH \
--paths '/api/investors,/api/investors/*' --query limit,cursor --auth-header X-KEI-API-Key
# Remote MCP server, from a catalog connector template. The template's kind
# (read from the catalog) picks the path; --kind mcp only checks it. The slug
# defaults to one derived from --label and cannot be changed later. Pass
# --mcp-url when the template has no URL, and --expected-issuer to pin the
# OAuth authorization server the MCP server must use.
kei connectors create --workspace Main --kind mcp --template custom_mcp --label "Grafana (prod)" \
--mcp-url https://mcp.grafana.example/mcp [--slug grafana_prod] [--expected-issuer https://login.example.com/tenant/v2.0]
kei connectors list --workspace Main # label, slug, kind, provider, state, account model, MCP URL
kei connectors list --workspace Main --manifests # adds each MCP connector's tool-manifest status
# An MCP connector's tools come only from its approved tool manifest. The
# runtime submits each manifest it observes as a candidate; review it here.
# "tools" is an alias for "manifests".
kei connectors manifests list CONNECTOR_ID --workspace Main
kei connectors manifests show CONNECTOR_ID --workspace Main # newest candidate, with its changes against the approved manifest
kei connectors manifests show CONNECTOR_ID MANIFEST_ID --workspace Main
kei connectors manifests approve CONNECTOR_ID MANIFEST_ID --workspace Main # org admins; shows the changes, then asks (or --yes)
kei connectors get CONNECTOR_ID --workspace Main # http_api: base URL, methods, paths, auth header, secret ref; mcp: slug, MCP URL, issuer
kei connectors delete CONNECTOR_ID --workspace Main --yes # revokes the connector
Missing fields are prompted for, with the schema's labels and defaults. A
secret field is never accepted as a flag: it is read from the terminal without
echo, or from stdin. The CLI seals it to the workspace's runtime
credential-sync keys (the envelope the console uses for model-profile keys),
and only the sealed copies are sent. The runtime stores the secret in the
tenant secret manager, and the connector becomes connected when it does. To
use a secret that is already in your secret manager, pass --credential-ref REF instead.
manifests show prints the candidate's tools and its changes since the
approved manifest: + added, - removed, ~ changed (description or
inputSchema). Tool names and descriptions come from the MCP server, so the
CLI replaces control characters in them before printing. manifests approve
needs the manifest's ID (it never picks one), prints the same changes, and
asks before calling the catalog's :approve method; pass --yes in scripts.
Approving a candidate replaces the connector's current approval.
An MCP connector's users connect their own accounts (OAuth, per user), so
create --template sends no credential, capability or policy fields: only
the template, the label, and the optional slug, MCP URL and expected issuer.
--template cannot be combined with --provider or the provider path's
flags. If the catalog accepts a create but does not store
--expected-issuer (a catalog that predates it), the CLI deletes the
connector it just created and fails, rather than leave one that would trust
any authorization server.
An http_api connector's methods choose its capabilities: each allowed method
declares the connector contract's http.<method> capability, so POST and
PATCH are writes (they need a kei built against kei-connector-contracts
v0.8.0 or later). The registration is checked against the contract before
anything is sent: an https public host without credentials, a relative path
pattern, and a valid header name. The key goes in Authorization: Bearer
unless --auth-header/--auth-scheme say otherwise; the header is always
redacted.
Manage policies
kei policy list --workspace Main
kei policy create --workspace Main --group Engineering --deny 'path:*.env'
kei policy test --workspace Main --as group:Engineering 'path:*.env'
kei policy delete deny-env --workspace Main --yes # ID or name
# Scope a policy to one agent, move it to another, or clear it.
kei policies create --workspace Main --name deny-env-bot --src-pattern 'harness:claude' --dst-pattern 'path:*.env' --effect deny --agent-id AGENT_ID
kei policies update deny-env-bot --workspace Main --agent-id OTHER_AGENT_ID
kei policies update deny-env-bot --workspace Main --agent-id '' # back to every agent
kei policies remains a deprecated alias for kei policy and still carries
update and import.
A policy with no agent applies to every agent in its workspace. With
--agent-id, it applies only to that agent, which must be a ready agent in the
same workspace; otherwise the server returns 400 with the reason. This needs a
catalog with HAI-513 deployed. Older catalogs reject agent_id with 400
"invalid request body".
Grant time-bound access
Standing access belongs on groups. For a just-in-time grant to one user or
agent, use kei access grant; the server caps the lifetime (7 days by default).
kei access grant create --workspace Main --principal user:USER_ID --target connector:linear --ttl 4h --reason 'incident 123'
kei access grant list --workspace Main
kei access grant get GRANT_ID --workspace Main
kei access grant revoke GRANT_ID --workspace Main --yes
Sync your Entra directory
An organization admin can have Kei sync the organization's groups and members
from Microsoft Entra app-role assignments or one security group. The settings live on your
organization in Kei (/api/v1/organizations/{org}/directory-sync); the
organization is the one your kei login token is bound to. You need three
GUIDs from Entra: your tenant ID, the client ID of the sync app registration
that has the Kei federated credential, and either the client ID of the app
whose app roles are synced or the object ID of the security group to sync.
# 1. Save the ids (the first set creates the configuration; dry run is on by default).
kei directory-sync set --tenant-id TENANT_GUID --client-id SYNC_APP_GUID --app-id ROLES_APP_GUID
# Or select one Entra security group by its object ID (mutually exclusive with --app-id).
kei directory-sync set --tenant-id TENANT_GUID --client-id SYNC_APP_GUID --group-id GROUP_GUID
# 2. Turn it on. Runs stay in dry run: nothing is written, each run records what it would do.
kei directory-sync enable
# 3. After the next sync interval, read what it would change.
kei directory-sync report # newest run: result, stats, changes
kei directory-sync report --limit 10 # the last 10 runs as a table
kei directory-sync get # settings and a last-run summary
# 4. When the report looks right, let it write (optionally create missing users).
kei directory-sync set --dry-run=false [--create-users=true]
# Stop syncing at any time.
kei directory-sync disable
set changes only the flags you pass (--tenant-id, --client-id,
--app-id or --group-id, --dry-run, --create-users, --revoke); booleans take
=true or =false. --revoke=true removes memberships that are no longer
assigned in Entra. set, enable, and disable ask for confirmation; pass
--yes in scripts. get --json and report --json print the API response.
get exits 1 with "No directory sync configured." until the first set.
Manage model profiles and the credential store
Model profiles and the credential store are organization resources in the
Kei API (/api/v1/organizations/{org}/...). kei credential-store is an
administrative kei CLI command; it calls the Kei API with your CLI login and is
not a kei-proxy call. The organization is the one your
kei login token is bound to. Pass --workspace (a name or ID) to work with a
workspace's profiles; omit it for organization-level profiles. Profile
arguments accept an ID or a display name; --agent takes an agent ID. Writes
require an organization admin.
kei credential-store get is read-only and prints only the store reference
and metadata. It never prints stored secret values or backend configuration,
matching aws secretsmanager describe-secret and gcloud secrets describe.
# Configure the credential store once (PUT creates it), then change it (PATCH).
kei credential-store put --secret-backend aws-secrets-manager \
--backend-config '{"region":"us-east-1","account_id":"123456789012"}' --secret-name-prefix kei
kei credential-store update --secret-name-prefix kei-prod
kei credential-store get
# A workspace's own store (an override of the organization's): put --workspace
# creates or replaces it from a full body; get --workspace reads it. Secrets of
# the workspace's connectors and profiles then live there.
kei credential-store put --workspace e2e --secret-backend aws-secrets-manager \
--backend-config '{"region":"us-east-1","account_id":"123456789012"}' --secret-name-prefix kei/e2e/store
kei credential-store get --workspace e2e
# A keyless profile does not require a credential store or runtime.
kei model-profiles create --workspace Main --display-name local --endpoint https://llm.internal/v1 \
--default-model llama-3 --auth-type none --workspace-default
# An API-key profile: the key is read from stdin (or typed without echo).
<secret-manager read> | kei model-profiles create --workspace Main --agent AGENT_ID \
--display-name openai --endpoint https://api.openai.com/v1 --default-model gpt-4.1 --auth-type api_key
<secret-manager read> | kei model-profiles update openai --workspace Main --rotate-key
kei model-profiles list [--workspace Main] [--json]
kei model-profiles get openai --workspace Main
kei model-profiles update openai --workspace Main --default-model gpt-4.1-mini
kei model-profiles set-default PROFILE # organization default (org-level profile)
kei model-profiles set-default openai --workspace Main # workspace default
kei model-profiles assign openai --workspace Main --agent AGENT_ID
kei model-profiles assignment --workspace Main --agent AGENT_ID
kei model-profiles unassign --workspace Main --agent AGENT_ID
kei model-profiles readiness openai --workspace Main # can runtimes reach the provider?
kei model-profiles test --endpoint https://api.openai.com/v1 # validate an endpoint, offline
kei model-profiles test openai --workspace Main # validate a stored profile's endpoint
kei model-profiles delete openai --workspace Main --yes
kei model-profiles list displays NAME, ID, MODEL, AUTH, STATUS, DEFAULT,
and ENDPOINT in a table. Revoked profiles remain visible and are marked in
STATUS. Use --json to print the API response as JSON.
An API key is never accepted as a flag and is never printed. The CLI seals it to the credential-sync keys of the runtimes in the profile's scope (the agent's runtimes, or the workspace's for a workspace default) with the same KMP1 envelope used for connector secrets; only the sealed copies are sent.
Create and update validate --endpoint locally with the same rule as the
console and API, before making a request. Invalid endpoints print the specific
failed rule and exit 1.
kei model-profiles test does what the console's model-profile Test
button does: it checks the endpoint and does not contact the provider. The
endpoint must be an https URL with a host and no embedded credentials, query,
or fragment (the console's and the API's rules; the CLI always applies the
production rule, so http is rejected). On success it prints
Configuration is valid; provider credentials will be verified by credential sync. and exits 0; otherwise it names the failed rule and exits 1, without
printing the URL. --endpoint makes no network request and needs no login;
test PROFILE reads the stored profile and checks its endpoint. To see whether
runtimes can actually reach the provider, use readiness.
Rotating a runtime credential into AWS Secrets Manager
When a runtime's Kubernetes secret is owned by an ExternalSecret that syncs from AWS Secrets Manager, the authoritative store is AWS. Rotate the credential and update the AWS secret in one pipeline:
kei bot credential --installation INSTALLATION_ID --rotate \
| ./scripts/update-aws-secret.sh kei-discord kei_runtime_token
scripts/update-aws-secret.sh reads the token only from stdin, updates
exactly one JSON property (default kei_runtime_token) of the secret's
SecretString with jq --rawfile, and calls aws secretsmanager put-secret-value using file:// input. The token is never placed in argv,
stdout, stderr, a temp filename, or shell history, and all other JSON fields are
preserved. The second argument is the property name; omit it to use the default
kei_runtime_token.
Preflight first — before you rotate. The rotation happens in the CLI before the script runs, so a failed destination check cannot prevent the rotation. If the write fails, the one-time token is consumed and you must rotate again. Verify the destination is healthy first:
# 1. Confirm the AWS secret exists and is a JSON object (do this BEFORE rotating).
aws secretsmanager get-secret-value --secret-id kei-discord \
--query SecretString --output text | jq -e 'type == "object"'
# 2. Confirm you can write to it.
aws secretsmanager describe-secret --secret-id kei-discord
The script prints only Updated property 'kei_runtime_token' on secret 'kei-discord'. on success. After the AWS secret is updated, the ExternalSecret
syncs it to the cluster; restart the runtime pod if it does not pick up the new
value automatically.
Key naming
The runtime reads the KEI_RUNTIME_TOKEN environment variable. In the
Discord/AWS deployment the token is stored in the AWS secret's lowercase
kei_runtime_token JSON property, which the ExternalSecret maps onto the
Kubernetes secret key the deployment consumes. Pass that property name as the
script's second argument (it is the default).
Release process
Releases are built and published by .github/workflows/release.yaml. For
the step-by-step runbook (prerequisites, watching a run, and what to do when
a run is skipped or fails), see docs/release.md.
How a release runs
The workflow has three jobs:
- Authorize manual release. Runs only for
workflow_dispatch. It fails unless the person dispatching hasadminpermission on the repository (a repository or organization owner). It is skipped on tag pushes. - Validate tag. Resolves the release tag and rejects anything that is
not
vMAJOR.MINOR.PATCH[-prerelease]. It sets the channel:prereleasefor tags containing-rc.,-alpha., or-beta., andstableotherwise. On a dispatch it also fails if the calculated tag already exists. - Release. Runs only when Validate tag succeeded. On a dispatch it
first creates and pushes the calculated tag. It then imports the GPG
signing key, checks the runner's AWS identity and bucket access, confirms
the pinned kei-proxy archive exists, and runs GoReleaser to build, sign,
and upload the artifacts. After that it uploads
install.sh, checks the published checksums and installer for leaked credentials, and, for stable releases only, writeslatest.txt.
Validate tag and Release run on the self-hosted kei-cli-release runner. The
runner Pod gets its AWS identity from IRSA, a dedicated IAM role for the
release service account. No AWS credentials, OIDC role ARN, or
configure-aws-credentials step appear in the repository or the workflow.
Cutting a release
Tag push. Tag a commit on main and push the tag:
git fetch origin
git tag vX.Y.Z origin/main
git push origin vX.Y.Z
Before #44, tag pushes silently skipped the Release job. Since #44, a tag push releases.
Manual dispatch. Let the workflow calculate the next version from the
highest stable vX.Y.Z tag, then create and push that tag itself:
gh workflow run release.yaml -R HaikeiLabs/kei-cli -f bump=patch # or minor, major
bump defaults to patch. A dispatch requires a repository or organization
owner. For anyone else, Authorize manual release fails and nothing is
released. A dispatch releases the head of the ref it is started on, which is
normally main.
The release environment
The Release job runs in the release GitHub environment. The environment
holds the GORELEASER_SIGNING_KEY secret, the ASCII-armored GPG private key
for kei-cli-releases@haikeilabs.com. The environment has no required
reviewers and no branch policy. The owner check for a dispatch lives in the
workflow, not in the environment.
Optional repository variables. None are set today, so the defaults apply:
| Variable | Default | Purpose |
|---|---|---|
AWS_REGION |
us-east-1 |
Region of the releases bucket |
AWS_S3_RELEASES_BUCKET |
kei-cli-releases |
Releases bucket |
The kei-proxy version is pinned by the KEI_PROXY_VERSION file at the repo
root — see the Bundled kei-proxy section below.
What gets published
Everything goes to s3://kei-cli-releases/kei-cli/. The public URL is
https://kei-cli-releases.s3.us-east-1.amazonaws.com/kei-cli/. Versioned
paths use the version without the leading v:
kei-cli/
install.sh curl installer; overwritten every release, no-cache
latest.txt latest stable version, e.g. 0.1.6; stable releases only
<version>/
kei-cli_<version>_macOS_arm64.tar.gz
kei-cli_<version>_macOS_x86_64.tar.gz
kei-cli_<version>_Linux_arm64.tar.gz
kei-cli_<version>_Linux_x86_64.tar.gz
kei-cli_<version>_checksums.txt SHA-256 of every artifact
kei-cli_<version>_checksums.txt.sig armored GPG signature of the checksums
kei-cli_<version>_source.tar.gz
Each archive contains kei, LICENSE, README.md, and the pinned kei-proxy
for the same platform. A prerelease publishes its <version>/ directory and
install.sh, but leaves latest.txt alone. Install a prerelease with
-v <version>. GitHub Releases are disabled; S3 is the only distribution
channel. Public reads come from the bucket policy, not object ACLs.
Verifying a release
BASE=https://kei-cli-releases.s3.us-east-1.amazonaws.com/kei-cli
curl -fsSL "$BASE/latest.txt" # stable releases: prints the new version
V=0.1.6 # the version you released
A="kei-cli_${V}_macOS_arm64.tar.gz"
curl -fsSLO "$BASE/$V/$A"
curl -fsSLO "$BASE/$V/kei-cli_${V}_checksums.txt"
shasum -a 256 -c --ignore-missing "kei-cli_${V}_checksums.txt" # expect: <archive>: OK
On Linux, use sha256sum -c --ignore-missing instead. A fresh install with
the curl installer is also an end-to-end check, because it verifies the same
checksum. See docs/release.md for the
signature check.
Upgrading from 0.1.5 or earlier (one time)
kei upgrade in 0.1.5 and earlier runs
go install github.com/HaikeiLabs/kei-cli@<version>. The entrypoint moved to
cmd/kei in 0.1.6 (#38), so that command no longer builds. Reinstall once,
using either method:
curl -fsSL "https://haikeilabs.com/install.sh" \
| sh -s -- -d "$HOME/.local/bin"
The old go install path produced a binary named kei-cli, so after
reinstalling you can remove any leftover $(go env GOPATH)/bin/kei-cli.
From 0.1.6 onward, kei upgrade [--version VERSION] upgrades from S3 and
replaces the running executable after checksum verification.
Bundled kei-proxy
Each CLI archive bundles the kei-proxy binary for its platform, pinned by
the KEI_PROXY_VERSION file at the repo root. During the release,
scripts/fetch-proxy.sh reads the pin from that file and downloads
s3://kei-cli-releases/kei-proxy/<version>/kei-proxy_<version>_<OS>_<ARCH>.tar.gz
using the runner's IRSA identity. <OS> is macOS or Linux, <ARCH> is
x86_64 or arm64, and <version> has no leading v, even when the pin
does. The Release job fails early if that proxy release does not exist, and
again if the fetched binary's --version does not match the pin. A CI
workflow (verify-proxy-pin.yaml) checks the same on every PR so the pin
cannot drift unnoticed.
To bump the pin when a new kei-proxy is released, edit the
KEI_PROXY_VERSION file at the repo root and change the version string
(one line, vX.Y.Z). Commit and push — the PR CI verifies the new pin is
valid and the artifact exists.
The installer installs kei-proxy when the archive contains it. Older
archives without it still install normally.
For a local test build with no upload and no signing:
AWS_S3_RELEASES_URL_BASE=https://kei-cli-releases.s3.us-east-1.amazonaws.com \
AWS_S3_RELEASES_BUCKET=kei-cli-releases AWS_S3_RELEASES_REGION=us-east-1 \
GORELEASER_SKIP_SIGN=1 make snapshot
Artifacts land in dist/. Run make clean to remove them.
Scope
The CLI currently supports login and logout, installation metadata, agent assignment, installation status, and self-upgrade for the Teams, Discord, and Slack platform identifiers. Runtime deployment workflows are intentionally outside the CLI's scope.
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
kei
command
|
|
|
internal
|
|
|
docs
Package docs fetches and prints pages from the public Haikei documentation site.
|
Package docs fetches and prints pages from the public Haikei documentation site. |
|
harness
Package harness renders Kei harness command policy bundles into each coding harness's native permission store.
|
Package harness renders Kei harness command policy bundles into each coding harness's native permission store. |
|
harnesspaths
Package harnesspaths resolves coding-harness config directories, honoring environment-variable overrides such as CODEX_HOME and CLAUDE_CONFIG_DIR (HAI-825).
|
Package harnesspaths resolves coding-harness config directories, honoring environment-variable overrides such as CODEX_HOME and CLAUDE_CONFIG_DIR (HAI-825). |