Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
112 commits
Select commit Hold shift + click to select a range
fd74e76
feat(agents): add the Console Agent API client and wire types
nimish-ks Oct 20, 2026
089df21
feat(proxy): add credential proxy primitives: CA store and trust, pro…
nimish-ks Oct 20, 2026
32ceff8
feat(config): support Agent-token principals, Phase AI config v3 and …
nimish-ks Oct 20, 2026
408ec92
feat(ai): add Agent skills and harness integrations for Claude Code, …
nimish-ks Oct 20, 2026
cc58732
feat(aiproxy): add the Phase AI proxy engine with PostgreSQL adapter,…
nimish-ks Oct 20, 2026
9629b72
feat(cli): add phase ai run, exec, serve and status, the hook-managed…
nimish-ks Oct 20, 2026
24aa2c7
docs: describe the Phase AI proxy and refresh the command list
nimish-ks Oct 20, 2026
4e629e2
fix(aiproxy): keep the audit spool healthy on Windows and skip POSIX …
nimish-ks Oct 20, 2026
71662d8
test(proxy): skip the NSS directory mode assertion on Windows
nimish-ks Oct 20, 2026
83392ee
fix(proxy): check every AWS operation selector for credential minting…
nimish-ks Oct 20, 2026
6539d0e
fix(aiproxy): redact request-derived audit fields instead of stopping…
nimish-ks Oct 20, 2026
ecc1d7d
fix(aiproxy): cancel tunneled requests with the engine and keep lifec…
nimish-ks Oct 20, 2026
6cf5e9c
fix(aiproxy): relay PostgreSQL startup settings and the server's para…
nimish-ks Oct 20, 2026
cb1ea7c
test(aiproxy): accept idempotent spool re-delivery in the redaction test
nimish-ks Oct 20, 2026
45fc5d2
fix(aiproxy): keep denials deliverable and fail closed on ambiguous m…
nimish-ks Oct 20, 2026
ce47861
fix(aiproxy): explain refused PostgreSQL startup settings and ignore …
nimish-ks Oct 20, 2026
57b2161
test(aiproxy): count distinct denials in the unauthenticated-request …
nimish-ks Oct 20, 2026
f2a31f4
feat(ai): remove phase ai serve
nimish-ks Oct 20, 2026
566b925
chore(proxy): drop the Java trust store and the go-pkcs12 dependency
nimish-ks Oct 20, 2026
8e891d8
chore(aiproxy): derive the SCRAM salted password with the standard li…
nimish-ks Oct 20, 2026
1fe107e
fix(ai): skip browser CA trust when the platform tool is missing inst…
nimish-ks Oct 22, 2026
f5617c9
ci: disable pacman's download sandbox inside the Arch enable container
nimish-ks Oct 22, 2026
80931b0
ci: capture enable output before matching it and drop the blocking tr…
nimish-ks Oct 22, 2026
f3ede0d
fix(ai): keep earlier decoys valid across hook daemon session recycle…
nimish-ks Oct 22, 2026
6c4dd01
Merge branch 'main' into feat--agents
nimish-ks Oct 22, 2026
fc0477c
fix(proxy): remove obsolete Phase AI CA roots on Windows with certutil
rohan-chaturvedi Oct 24, 2026
46e584a
build: spell the replaces relation per packager
nimish-ks Oct 26, 2026
854f285
Merge remote-tracking branch 'origin/feat--agents' into fix--windows-…
nimish-ks Oct 26, 2026
5128282
fix(proxy): tighten the Windows rotation comments
nimish-ks Oct 26, 2026
0d6e3c7
ci: prove Windows CA rotation on a real user Root store (temporary)
nimish-ks Oct 26, 2026
9800e6c
fix(proxy): give Windows PowerShell its own module path for the trust…
nimish-ks Oct 26, 2026
8205fcc
ci: the Windows rotation check needs no dialog handling on a runner
nimish-ks Oct 26, 2026
5efa78f
ci: drop the temporary Windows rotation check
nimish-ks Oct 26, 2026
aaae4de
Merge pull request #324 from phasehq/fix--windows-ca-rotation
nimish-ks Oct 26, 2026
89973c8
fix(ai): route Claude Code shells on Windows with a port probe
rohan-chaturvedi Oct 26, 2026
201937d
fix(ai): install the OpenCode plugin where OpenCode loads it on Windows
rohan-chaturvedi Oct 26, 2026
74980cc
fix(proxy): keep inherited environment names that are not shell ident…
rohan-chaturvedi Oct 26, 2026
502163c
test(ai): isolate USERPROFILE in the Devin hook tests
rohan-chaturvedi Oct 26, 2026
51375b7
fix(ai): let Codex, Cursor and Devin reach phase.invalid on Windows
rohan-chaturvedi Oct 26, 2026
33da4f4
fix(ai): run Phase hooks on Windows in any shell
rohan-chaturvedi Oct 26, 2026
731c187
fix(ai): treat Phase calls mixed with other work inside sh -c or eval…
nimish-ks Oct 26, 2026
a9a1df5
fix(proxy): block CodeArtifact GetAuthorizationToken at its real path
nimish-ks Oct 26, 2026
0d7fa16
fix(ai): install the OpenCode plugin under ~/.config on Windows too
nimish-ks Oct 26, 2026
97835f1
fix(ai): keep fresh leases, stop the daemon after disable, quote Code…
nimish-ks Oct 26, 2026
31b68d8
fix(ai): read every -Command and /c word as script and close the disa…
nimish-ks Oct 26, 2026
0ae53c5
fix(proxy): limit the CodeArtifact token rule to CodeArtifact requests
nimish-ks Oct 26, 2026
69eb8bf
fix(ai): follow Cursor's Windows terminal profile when quoting the wr…
nimish-ks Oct 26, 2026
aa49821
fix(ai): parse fish, PowerShell and cmd scripts by their own rules an…
nimish-ks Oct 26, 2026
77ae04c
fix(ai): read Cursor's terminal profile from its JSONC settings and p…
nimish-ks Oct 26, 2026
73f1b86
fix(ai): keep each fish script separate and read attached -c values
nimish-ks Oct 26, 2026
0d6acc3
fix(ai): resolve Cursor profile paths and comments after trailing commas
nimish-ks Oct 26, 2026
ed8e51b
fix(ai): enable other harnesses when the Codex CLI is not on PATH
rohan-chaturvedi Oct 27, 2026
a0d5803
Merge feat--agents into fix--windows-harness-env
rohan-chaturvedi Oct 27, 2026
474b9e6
fix(ai): give Devin and OpenCode a wrapper PowerShell can run on Windows
rohan-chaturvedi Oct 27, 2026
b9fecd1
fix(ai): catch Invoke-WebRequest calls inside quoted PowerShell scripts
rohan-chaturvedi Oct 27, 2026
7269d9f
fix(ai): leave an up-to-date Codex adapter alone when enabling anothe…
rohan-chaturvedi Oct 27, 2026
ccd687b
refactor(ai): trim the Windows harness fixes
nimish-ks Oct 27, 2026
c835d87
Merge pull request #326 from phasehq/fix--windows-harness-env
nimish-ks Oct 27, 2026
c242c38
feat(ai): accept key-bearing v2 Agent tokens
nimish-ks Oct 28, 2026
775ee68
feat(ai): version key-bearing Agent tokens as v1
nimish-ks Oct 28, 2026
64795ca
fix(ai): follow the harness shell for the Windows wrapper and keep ex…
nimish-ks Oct 27, 2026
cbe1b99
fix(ai): keep user handlers that share a group with Phase's Devin hooks
nimish-ks Oct 28, 2026
e230cbd
fix(ai): recognize Devin hooks whose helper path needed POSIX quoting
nimish-ks Oct 28, 2026
57ccff2
fix(ai): do not claim or wipe a Claude plugin directory Phase does no…
nimish-ks Oct 28, 2026
b7af7d5
fix(ai): treat 2>&1 and 2>&- as redirections in the shell guard
nimish-ks Oct 28, 2026
dc8ce7e
fix(ai): run the OpenCode 2.x command guard for the shell tool
nimish-ks Oct 28, 2026
6d1b1fc
fix(ai): install the OpenCode plugin and skill where OpenCode reads them
nimish-ks Oct 28, 2026
b7d1ddd
fix(proxy): block EC2, Lightsail, Redshift Serverless and S3 Express …
nimish-ks Oct 28, 2026
aca324e
fix(proxy): stop path-scoped host rules matching dot segments
nimish-ks Oct 28, 2026
9fe7e68
fix(proxy): accept repeated values for ordinary signed AWS headers
nimish-ks Oct 28, 2026
af8d79b
fix(proxy): keep the ETag on redacted AWS responses
nimish-ks Oct 28, 2026
7652c6c
fix(proxy): return S3 objects stored with gzip encoding
nimish-ks Oct 28, 2026
49a4fb2
fix(proxy): serialize proxy CA load and renewal across processes
nimish-ks Oct 28, 2026
6570921
fix(proxy): refuse PostgreSQL statements that create a role or set a …
nimish-ks Oct 28, 2026
9f77476
fix(proxy): send re-signed AWS requests only over HTTPS
nimish-ks Oct 28, 2026
2c1eba3
fix(proxy): close PostgreSQL connections when the session expires
nimish-ks Oct 28, 2026
e6a9934
fix(proxy): stop treating public identifiers as audit secrets
nimish-ks Oct 28, 2026
e212c59
fix(ai): keep a sandbox's own CA paths when selecting a relay Connection
nimish-ks Oct 28, 2026
f965135
fix(config): replace Phase CLI config files atomically
nimish-ks Oct 28, 2026
7c83f71
fix(ai): claim the hook runtime lock under an OS file lock
nimish-ks Oct 28, 2026
557132f
fix(ai): serialize environment publication with stale-routing retirement
nimish-ks Oct 28, 2026
d8c0b01
fix(ai): let setup stop a hook daemon while it recycles its session
nimish-ks Oct 28, 2026
f1f7d79
fix(ai): treat a zombie hook daemon as exited
nimish-ks Oct 28, 2026
beebd3f
fix(ai): lease a session to a hook daemon that is still starting
nimish-ks Oct 28, 2026
51e14d8
fix(ai): keep hooks from starting a daemon while phase ai enable runs
nimish-ks Oct 28, 2026
7bf5102
fix(ai): adopt a hook daemon environment that Claude inherits
nimish-ks Oct 28, 2026
b848f34
fix(ai): find the recycle cause past any number of retries
nimish-ks Oct 28, 2026
727a7f8
test(config): pace the concurrent ai.json readers
nimish-ks Oct 28, 2026
00ea122
fix(ai): lease a session to a hook daemon while it recycles its session
nimish-ks Oct 28, 2026
3ef9c61
test(ai): count Devin guard handlers by their exact command
nimish-ks Oct 28, 2026
cff5061
fix(ai): remove the OpenCode plugin where enable installed it
nimish-ks Oct 28, 2026
20ace8e
fix(ai): apply every selected Connection value except inherited CA paths
nimish-ks Oct 28, 2026
8be5da7
fix(proxy): keep a request's trailing slash when matching a path-scop…
nimish-ks Oct 28, 2026
033d5dc
fix(proxy): read only DO blocks and function bodies as PostgreSQL code
nimish-ks Oct 28, 2026
b39f964
fix(proxy): read PostgreSQL code bodies and comments as the server does
nimish-ks Oct 28, 2026
54b8b96
fix(ai): find an earlier build's OpenCode plugin beside its skill
nimish-ks Oct 28, 2026
215b5e5
fix(ai): record an OpenCode plugin before enable writes it
nimish-ks Oct 28, 2026
54c7885
fix(config): never replace an unreadable ai.json when recording an Op…
nimish-ks Oct 28, 2026
ebffdcd
fix(ai): check OpenCode plugin ownership before recording its path
nimish-ks Oct 28, 2026
fce019e
fix(proxy): read a U& identifier and its UESCAPE clause as one Postgr…
nimish-ks Oct 28, 2026
2eb303f
fix(ai): leave nested scripts with expansions out of Phase management
nimish-ks Oct 28, 2026
4c54477
fix(ai): manage a curl or wget call only when every target is Phase
nimish-ks Oct 28, 2026
d49b49d
fix(ai): keep a caret-escaped redirection from hiding a cmd separator
nimish-ks Oct 28, 2026
a9fe4d4
fix(ai): keep Windows-only shell quoting out of Phase management
nimish-ks Oct 28, 2026
a669e72
fix(ai): leave a curl or wget call with a redirected config env ambient
nimish-ks Oct 28, 2026
ec73e08
fix(ai): parse curl and wget options in order to prove the target
nimish-ks Oct 28, 2026
b9eebd8
fix(ai): manage only a single plain curl or wget call to phase.invalid
nimish-ks Oct 28, 2026
c96d894
fix(ai): read only an unquoted number before a redirection as a descr…
nimish-ks Oct 28, 2026
3c126d1
fix(ai): keep commands with PowerShell typographic quotes unmanaged
nimish-ks Oct 28, 2026
2149840
fix(ai): keep curl or wget calls with unquoted globs unmanaged
nimish-ks Oct 28, 2026
1cbd916
fix(config): retry a config read that races an atomic save on Windows
nimish-ks Oct 28, 2026
89841bb
Merge pull request #327 from phasehq/feat--agent-token-keys
nimish-ks Oct 29, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions .github/scripts/ai-enable-container.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#!/bin/sh
# Runs inside a stock Linux container as root: creates an unprivileged user and
# checks `phase ai enable` both without NSS certutil (browser trust must be
# skipped, not fatal) and with it installed (trust must land in the per-user
# NSS database). Env: PHASE (binary path), INSTALL (command that installs certutil).
set -eu
PHASE="${PHASE:?}"; INSTALL="${INSTALL:?}"
. /etc/os-release 2>/dev/null || true; echo "os: ${PRETTY_NAME:-unknown}"
if command -v useradd >/dev/null 2>&1; then useradd -m -s /bin/sh tester; else adduser -D tester; fi
as_user() {
if command -v runuser >/dev/null 2>&1; then runuser -u tester -- sh -c "export HOME=/home/tester; cd /home/tester; $1"
elif command -v su >/dev/null 2>&1; then su tester -c "export HOME=/home/tester; cd /home/tester; $1"
elif command -v setpriv >/dev/null 2>&1; then setpriv --reuid=tester --regid=tester --init-groups sh -c "export HOME=/home/tester; cd /home/tester; $1"
else chroot --userspec=tester:tester / sh -c "export HOME=/home/tester; cd /home/tester; $1"; fi
}
fail() { echo "FAIL: $*" >&2; exit 1; }
if command -v certutil >/dev/null 2>&1; then fail "image already has certutil; the stock run needs it absent"; fi

echo "--- stock image: browser trust must be skipped without failing"
out="$(as_user "$PHASE ai enable --harness claude-code --mask" 2>&1)" || fail "enable failed without certutil: $out"
echo "$out" | grep -q "Browser CA trust skipped" || fail "missing skip notice: $out"
echo "$out" | grep -q "Phase AI enabled for Claude Code" || fail "harness not enabled: $out"
if command -v script >/dev/null 2>&1; then
echo "--- stock image, interactive terminal: the trust prompt must not appear"
out="$(as_user "printf 'y\n' | script -qec '$PHASE ai enable --harness claude-code --mask' /dev/null" 2>&1 | tr -d '\r')" || fail "interactive enable failed: $out"
echo "$out" | grep -q "Trust the Phase AI CA" && fail "prompt shown although certutil is missing: $out"
echo "$out" | grep -q "Browser CA trust skipped" || fail "missing skip notice in interactive run: $out"
fi
echo "--- stock image: an explicit --trust-ca fails with the package hint"
if out="$(as_user "$PHASE ai enable --harness claude-code --mask --trust-ca" 2>&1)"; then fail "explicit --trust-ca succeeded without certutil: $out"; fi
echo "$out" | grep -q "NSS certutil is not installed (package" || fail "missing package hint: $out"

echo "--- installing certutil: $INSTALL"
sh -c "$INSTALL" >/dev/null 2>&1 || fail "could not install certutil"
command -v certutil >/dev/null 2>&1 || fail "certutil still missing after install"
out="$(as_user "$PHASE ai enable --harness claude-code --mask --trust-ca" 2>&1)" || fail "enable --trust-ca failed: $out"
echo "$out" | grep -q "trusted in the Linux current-user NSS database" || fail "CA not trusted: $out"
as_user "certutil -L -d sql:/home/tester/.pki/nssdb" | grep -q "Phase AI Proxy CA" || fail "NSS database does not list the Phase CA"
second="$(as_user "$PHASE ai enable --harness claude-code --mask --trust-ca" 2>&1)" || fail "second enable --trust-ca failed: $second"
echo "$second" | grep -q "already trusted" || fail "second trust run not idempotent: $second"
status="$(as_user "$PHASE ai status" 2>&1)" || fail "status failed: $status"
echo "$status" | grep -q "Phase AI: enabled" || fail "status not enabled: $status"
echo "OK: ${PRETTY_NAME:-unknown}"
37 changes: 37 additions & 0 deletions .github/scripts/ai-enable-smoke.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#!/usr/bin/env bash
# Runs `phase ai enable` end to end on a CI runner and checks the result.
# usage: ai-enable-smoke.sh <phase-binary> <no-trust|trust-nss>
set -euo pipefail
PHASE="$1"; MODE="$2"
HOME_DIR="${HOME:?}"
fail() { echo "FAIL: $*" >&2; exit 1; }

case "$MODE" in
no-trust)
output="$("$PHASE" ai enable --harness claude-code --mask --trust-ca=false </dev/null 2>&1)" || fail "enable exited non-zero: $output"
echo "$output"
echo "$output" | grep -q "Phase AI CA ready" || fail "CA was not created"
echo "$output" | grep -q "Phase AI enabled for Claude Code" || fail "harness was not enabled"
;;
trust-nss)
command -v certutil >/dev/null || fail "certutil is required for trust-nss"
output="$("$PHASE" ai enable --harness claude-code --mask --trust-ca </dev/null 2>&1)" || fail "enable --trust-ca exited non-zero: $output"
echo "$output"
echo "$output" | grep -q "trusted in the Linux current-user NSS database" || fail "CA was not trusted in the NSS database"
certutil -L -d "sql:$HOME_DIR/.pki/nssdb" | grep -q "Phase AI Proxy CA" || fail "NSS database does not list the Phase CA"
# A second run must be idempotent.
second="$("$PHASE" ai enable --harness claude-code --mask --trust-ca </dev/null 2>&1)" || fail "second enable --trust-ca exited non-zero: $second"
echo "$second" | grep -q "already trusted" || fail "second trust run was not idempotent: $second"
;;
*) fail "unknown mode $MODE" ;;
esac

status="$("$PHASE" ai status 2>&1)"
echo "$status"
echo "$status" | grep -q "Phase AI: enabled" || fail "status does not report enabled"
echo "$status" | grep -q "^CA:" || fail "status does not report the CA"
[ -f "$HOME_DIR/.phase/ai/ca.pem" ] || fail "ca.pem missing"
grep -q "PRIVATE KEY" "$HOME_DIR/.phase/ai/ca.pem" && fail "ca.pem contains private material"
[ -f "$HOME_DIR/.claude/skills/phase-cli-ai-relay/SKILL.md" ] || fail "runtime skill missing"
[ -f "$HOME_DIR/.claude/skills/phase-cli-app-secrets/SKILL.md" ] || fail "secrets skill missing"
echo "OK: $MODE"
76 changes: 76 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,79 @@ jobs:
- name: Build (smoke test)
working-directory: src
run: go build ./...

enable-smoke:
name: phase ai enable (${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
env:
CGO_ENABLED: "0"
steps:
- uses: actions/checkout@v7

- name: Set up Go
uses: actions/setup-go@v7
with:
go-version: "1.25.x"
cache-dependency-path: src/go.sum

- name: Build
working-directory: src
shell: bash
run: go build -o ../phase-cli${{ runner.os == 'Windows' && '.exe' || '' }} .

- name: Enable without touching the trust store
shell: bash
run: ./.github/scripts/ai-enable-smoke.sh ./phase-cli${{ runner.os == 'Windows' && '.exe' || '' }} no-trust

- name: Trust the CA in the per-user NSS database
if: runner.os == 'Linux'
run: |
sudo apt-get update -qq && sudo apt-get install -y -qq libnss3-tools
./.github/scripts/ai-enable-smoke.sh ./phase-cli trust-nss

# The macOS keychain and the Windows user Root store need a consent
# dialog that a headless runner cannot show (the command blocks until
# the job times out), so those two trust paths are covered by the unit
# tests with a recorded command runner and by manual QA.

enable-containers:
name: phase ai enable (${{ matrix.image }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- image: fedora:41
install: dnf -y -q install nss-tools
- image: debian:12
install: apt-get -qq update && DEBIAN_FRONTEND=noninteractive apt-get -qq install -y libnss3-tools
- image: ubuntu:24.04
install: apt-get -qq update && DEBIAN_FRONTEND=noninteractive apt-get -qq install -y libnss3-tools
- image: alpine:3.20
install: apk add -q nss-tools
- image: archlinux:latest
install: pacman -Sy --noconfirm --disable-sandbox nss
env:
CGO_ENABLED: "0"
steps:
- uses: actions/checkout@v7

- name: Set up Go
uses: actions/setup-go@v7
with:
go-version: "1.25.x"
cache-dependency-path: src/go.sum

- name: Build a static Linux binary
working-directory: src
run: GOOS=linux GOARCH=amd64 go build -o ../phase-cli .

- name: Enable inside a stock ${{ matrix.image }} container
run: |
docker run --rm -v "$PWD:/work:ro" -e PHASE=/work/phase-cli -e INSTALL='${{ matrix.install }}' \
${{ matrix.image }} sh /work/.github/scripts/ai-enable-container.sh

1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Go build output
bin/
dist/
/src/phase

# Go module
vendor/
Expand Down
11 changes: 9 additions & 2 deletions .goreleaser.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,15 @@ nfpms:
- rpm
- apk
bindir: /usr/bin
replaces:
- "phase (<< 2.0.0)"
# Each packager spells version relations differently; nfpm now rejects
# the deb form for rpm.
overrides:
deb:
replaces:
- "phase (<< 2.0.0)"
rpm:
replaces:
- "phase < 2.0.0"
ids:
- phase-cli

Expand Down
43 changes: 26 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,13 @@ Commands:
users switch 🪄 Switch between Phase users, orgs and hosts
users logout 🏃 Logout from phase-cli
users keyring 🔐 Display information about the Phase keyring
ai enable 🪄 Enable AI integrations and configure secret visibility
ai disable 🚫 Disable AI integrations and remove skill docs
ai enable 🪄 Enable the local Phase AI credential proxy
ai disable 🚫 Disable AI integrations
ai status 📡 Show Phase AI setup and proxy health
ai skill 📄 Print the Phase AI skill document
ai run 🚀 Launch an AI Agent behind the Phase AI credential proxy
ai exec ⚡ Execute one command behind a transient Phase AI credential proxy
relay start 🛰️ Run a Phase AI relay for remote Agent sandboxes
console 🖥️ Open the Phase Console in your browser
docs 📖 Open the Phase CLI Docs in your browser
completion ⌨️ Generate the autocompletion script for the specified shell
Expand All @@ -51,7 +55,7 @@ Flags:

## Features

- **End-to-end encryption** — secrets are encrypted client-side before leaving your machine
- **End-to-end encryption** — App secrets in E2E mode are encrypted client-side before leaving your machine
- **Secret types** — `config` (non-sensitive), `secret` (sensitive), and `sealed` (write-only) with enforced visibility rules
- **`phase run`** — inject secrets as environment variables into any command without code changes
- **`phase shell`** — launch a sub-shell (bash, zsh, fish, etc.) with secrets preloaded
Expand All @@ -62,9 +66,9 @@ Flags:
- **Path-based organisation** — organise secrets in hierarchical paths for monorepos and microservices
- **Tagging** — tag secrets and filter operations by tag
- **Random secret generation** — generate hex, alphanumeric, base64, base64url, 128-bit, or 256-bit keys on create or update
- **AI agent integration** — skill-based integration with Claude Code, Cursor, VS Code Copilot, Codex, and OpenCode with automatic value redaction and safety guardrails
- **Multiple auth methods** — web-based login, personal access tokens, service account tokens, and AWS IAM identity auth
- **Multi-user & multi-org** — switch between Phase accounts, orgs, and self-hosted instances
- **AI Agent proxy** — workflow-scoped third-party Integration credentials for HTTP, PostgreSQL, and AWS without exposing live material
- **Multiple auth methods** — browser login, personal access tokens, service account tokens, Agent tokens, and AWS IAM identity auth
- **Multi-user & multi-org** — switch between Phase accounts, orgs and self-hosted instances
- **OS keyring integration** — credentials stored in macOS Keychain, GNOME Keyring, or Windows Credential Manager
- **Multiple environments** — dev, staging, production, and custom environments with per-project defaults via `phase init`

Expand Down Expand Up @@ -125,15 +129,22 @@ phase run 'go run main.go'
phase run --env production 'python manage.py runserver'
```

### AI integration
## AI Agent proxy

Enable AI agent support (installs a skill doc for your AI coding tool):
Phase AI gives coding agents (Claude Code, Cursor, Codex, OpenCode, Devin, VS Code Copilot) workflow-scoped access to third-party services (HTTP APIs, AWS, PostgreSQL) without ever handing them a live credential. The agent's process receives decoys; a local proxy on `127.0.0.1:9543` (PostgreSQL adapter on `9544`) swaps them for the real credential on the way out, enforces the Connections approved in the Phase Console, and blocks calls that would mint new credentials.

```bash
phase ai enable
phase ai enable --harness claude-code # install the hook/plugin and skills for one harness; repeat for others
phase ai status # setup, proxy health, Connections, CA expiry
phase ai disable
```

This installs the Phase skill to your chosen AI tool (Claude Code, Cursor, VS Code Copilot, Codex, or OpenCode) and configures secret visibility. Sealed secrets are never revealed to AI agents regardless of settings.
- **Claude Code and OpenCode** get the Phase environment automatically: provider CLIs just work while the proxy runs, and fall back to failing on the decoy (never to ambient credentials) when it is down.
- **Cursor, Codex and Devin** run Phase-managed commands explicitly with `phase ai exec -- <command>` (`--connection <id>` picks one of several identities for a service); `phase ai run -- <agent>` launches a harness inside the environment.
- **Remote sandboxes** that cannot run the CLI use a relay: `phase relay start --advertise-host <host> --tls-cert <pem> --tls-key <pem>` on a machine logged in with an Agent token prints one line that bootstraps a sandbox over curl.
- Two skills are installed per harness: `phase-cli-ai-relay` (the proxy, Connections and requests) and `phase-cli-app-secrets` (Phase app secrets). Agents reach the proxy's control API at `http://phase.invalid` and can request new Connections, which a human approves in the Console.

`phase ai enable` creates a per-installation CA (valid 90 days, renewed automatically) and offers to trust it for the current user; it never changes system-wide trust or requires sudo. Command-line tools and SDKs trust the CA through the proxy environment either way; the user trust store matters for browsers and for Go programs on macOS and Windows. On Linux the offer needs NSS certutil (`libnss3-tools` on Debian/Ubuntu, `nss-tools` on Fedora/RHEL/Alpine, `nss` on Arch, `mozilla-nss-tools` on openSUSE) and is skipped with a hint when it is missing.

## Development

Expand All @@ -143,18 +154,16 @@ This installs the Phase skill to your chosen AI tool (Claude Code, Cursor, VS Co

### Project structure

```
```text
src/
├── main.go # Entrypoint
├── cmd/ # Cobra command definitions
├── internal/ # Agent API, runtime, and proxy internals
├── pkg/
│ ├── ai/ # AI agent detection, skill doc, redaction
│ ├── config/ # Config file handling (~/.phase/, .phase.json)
│ ├── display/ # Output formatting (tree view, tables)
│ ├── errors/ # Error types
│ ├── ai/ # Agent detection and embedded skill
│ ├── config/ # Config file handling
│ ├── keyring/ # OS keyring integration
│ ├── phase/ # Phase client helpers (auth, init)
│ ├── util/ # Misc utilities (color, spinner, browser)
│ ├── proxy/ # CA, environment, routing, and injection helpers
│ └── version/ # Version constant
└── go.mod
```
Expand Down
2 changes: 1 addition & 1 deletion src/cmd/ai.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
var aiCmd = &cobra.Command{
Use: "ai",
Short: "🥷 Integrate Phase with AI Agents",
Long: "Configure how AI coding agents interact with your Phase secrets.",
Long: "Run AI coding agents through Phase's workflow-scoped local credential proxy.",
}

func init() {
Expand Down
Loading
Loading