Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
24 changes: 14 additions & 10 deletions docs/CLAUDE-DEV-ENVIRONMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -239,12 +239,13 @@ Both are stored in the host Secret Service keyring, retrieved by the launcher, a

### Secret naming in keyring

| Secret | Service | Account |
| ----------------------- | ------------ | -------------- |
| Claude Code OAuth token | `claude-dev` | `claude-oauth` |
| GitHub Token | `claude-dev` | `github-token` |
| Secret | Service | Account | Key |
| ----------------------- | ------------ | ---------- | --------------------------------- |
| Claude Code OAuth token | `claude-dev` | `claude` | `oauth` (default) |
| GitHub Token | `claude-dev` | `github` | repo-specific (e.g. `molim-token`) |
| DeepSeek API key | `claude-dev` | `deepseek` | `apikey` (default) |

Both use the `claude-dev` service namespace. The `account` attribute distinguishes them.
All entries use the `claude-dev` service namespace. `account` identifies the target system; `key` identifies the credential within that system and is configured per-repo in `claude-dev.env` via `CLAUDE_DEV_KEYRING_GITHUB_KEY`, `CLAUDE_DEV_KEYRING_CLAUDE_KEY`, and `CLAUDE_DEV_KEYRING_DEEPSEEK_KEY`. Multiple GitHub repos can coexist in one keyring by using distinct `key` values (e.g., `molim-token`, `otherrepo-token`).

### One-time bootstrap: Claude Code OAuth token

Expand All @@ -266,26 +267,29 @@ Procedure:

```bash
secret-tool store --label='Claude Code OAuth' \
service claude-dev account claude-oauth
service claude-dev account claude key oauth
```

### One-time bootstrap: GitHub Token

Generate a fine-grained Personal Access Token in GitHub and store it directly in the keyring:

```bash
secret-tool store --label='Claude Code GitHub Token' \
service claude-dev account github-token
secret-tool store --label='Claude Code GitHub Token ({repo})' \
service claude-dev account github key {repo}-token
```

Replace `{repo}` with the actual repository name (e.g., `molim`). The `{repo}-token` value must match `CLAUDE_DEV_KEYRING_GITHUB_KEY` in that repo's `claude-dev.env`. Using distinct values per repo enables simultaneous sessions against different repositories from the same keyring.

Github token scoping and permissions are explained further in **GitHub Token scoping** section.

### Retrieval

The launcher retrieves both tokens at runtime:

```bash
CLAUDE_CODE_OAUTH_TOKEN=$(secret-tool lookup service claude-dev account claude-oauth)
GITHUB_TOKEN_=$(secret-tool lookup service claude-dev account github-token)
CLAUDE_CODE_OAUTH_TOKEN=$(secret-tool lookup service claude-dev account claude key oauth)
GITHUB_TOKEN_=$(secret-tool lookup service claude-dev account github key {repo}-token)
```

If either lookup returns empty, the launcher fails fast with a bootstrap-procedure pointer.
Expand Down
3 changes: 3 additions & 0 deletions docs/CURRENT-STATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,9 @@ Host/project launcher variables include at least:
* `CLAUDE_DEV_WORKSPACE_TMPFS_SIZE`
* `CLAUDE_DEV_PROXY_SOCKET_CONTAINER_PATH`
* `CLAUDE_DEV_PROXY_PORT`
* `CLAUDE_DEV_KEYRING_GITHUB_KEY`
* `CLAUDE_DEV_KEYRING_CLAUDE_KEY`
* `CLAUDE_DEV_KEYRING_DEEPSEEK_KEY`

---

Expand Down
10 changes: 9 additions & 1 deletion scripts/claude-dev.env
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ CLAUDE_DEV_LLM_BACKEND=anthropic
# Used only when CLAUDE_DEV_LLM_BACKEND=deepseek or the launcher receives
# --backend deepseek / --deepseek.
# The API key is NOT stored here; it is read from Secret Service:
# service=claude-dev account=deepseek-apikey
# service=claude-dev account=deepseek key=$CLAUDE_DEV_KEYRING_DEEPSEEK_KEY
CLAUDE_DEV_DEEPSEEK_BASE_URL=https://api.deepseek.com/anthropic
CLAUDE_DEV_DEEPSEEK_MODEL=deepseek-v4-flash
CLAUDE_DEV_DEEPSEEK_OPUS_MODEL=deepseek-v4-pro[1m]
Expand All @@ -33,6 +33,14 @@ CLAUDE_DEV_DEEPSEEK_HAIKU_MODEL=deepseek-v4-flash
CLAUDE_DEV_DEEPSEEK_SUBAGENT_MODEL=deepseek-v4-flash
CLAUDE_DEV_DEEPSEEK_EFFORT_LEVEL=max

# Keyring entry identifiers. The launcher uses secret-tool with three
# attributes: service=claude-dev, account=<system>, key=<value-below>.
# Each repo's claude-dev.env sets its own GITHUB key so per-repo tokens
# can coexist in the same keyring.
CLAUDE_DEV_KEYRING_GITHUB_KEY=molim-token
CLAUDE_DEV_KEYRING_CLAUDE_KEY=oauth
CLAUDE_DEV_KEYRING_DEEPSEEK_KEY=apikey

# Needs to be a file directly in /run to support the /run tmpfs.
CLAUDE_DEV_PROXY_SOCKET_CONTAINER_PATH=/run/proxy.sock
CLAUDE_DEV_PROXY_PORT=8080
Expand Down
15 changes: 10 additions & 5 deletions scripts/claude-dev.sh
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,10 @@ require_var ENVOY_ADMIN_CONTAINER_PORT
require_var ENVOY_ADMIN_ADDRESS
require_var ENVOY_SOCKET_CONTAINER_PATH

require_var CLAUDE_DEV_KEYRING_GITHUB_KEY
require_var CLAUDE_DEV_KEYRING_CLAUDE_KEY
require_var CLAUDE_DEV_KEYRING_DEEPSEEK_KEY

# Backend resolution order:
# 1. explicit CLI option
# 2. claude-dev.env / inherited environment
Expand Down Expand Up @@ -314,29 +318,30 @@ fi
# ----------------------------------------------------------------------
keyring_lookup() {
local account="$1"
local key="$2"
local value
value="$(secret-tool lookup service claude-dev account "$account" || true)"
value="$(secret-tool lookup service claude-dev account "$account" key "$key" || true)"
if [[ -z "$value" ]]; then
die "no secret found in keyring for service=claude-dev account=${account}. See CLAUDE-DEV-ENVIRONMENT.md for the bootstrap procedure."
die "no secret found in keyring for service=claude-dev account=${account} key=${key}. See CLAUDE-DEV-ENVIRONMENT.md for the bootstrap procedure."
fi
printf '%s' "$value"
}

GITHUB_TOKEN_="$(keyring_lookup github-token)"
GITHUB_TOKEN_="$(keyring_lookup github "$CLAUDE_DEV_KEYRING_GITHUB_KEY")"
export GITHUB_TOKEN_

BACKEND_DOCKER_ENV=()
case "$CLAUDE_DEV_LLM_BACKEND" in
anthropic)
CLAUDE_CODE_OAUTH_TOKEN="$(keyring_lookup claude-oauth)"
CLAUDE_CODE_OAUTH_TOKEN="$(keyring_lookup claude "$CLAUDE_DEV_KEYRING_CLAUDE_KEY")"
export CLAUDE_CODE_OAUTH_TOKEN
BACKEND_DOCKER_ENV=(
-e CLAUDE_CODE_OAUTH_TOKEN
)
;;

deepseek)
ANTHROPIC_AUTH_TOKEN="$(keyring_lookup deepseek-apikey)"
ANTHROPIC_AUTH_TOKEN="$(keyring_lookup deepseek "$CLAUDE_DEV_KEYRING_DEEPSEEK_KEY")"

ANTHROPIC_BASE_URL="${CLAUDE_DEV_DEEPSEEK_BASE_URL:-https://api.deepseek.com/anthropic}"
ANTHROPIC_MODEL="${CLAUDE_DEV_DEEPSEEK_MODEL:-deepseek-v4-flash}"
Expand Down
Loading