Help us shape the future of Go! Take the 2026 Go Developer Survey

kei-cli

module
v0.2.13 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 23, 2026 License: MIT

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.

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:

  1. Authorize manual release. Runs only for workflow_dispatch. It fails unless the person dispatching has admin permission on the repository (a repository or organization owner). It is skipped on tag pushes.
  2. Validate tag. Resolves the release tag and rejects anything that is not vMAJOR.MINOR.PATCH[-prerelease]. It sets the channel: prerelease for tags containing -rc., -alpha., or -beta., and stable otherwise. On a dispatch it also fails if the calculated tag already exists.
  3. 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, writes latest.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
app
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).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL