diff --git a/docs/CLAUDE-DEV-ENVIRONMENT.md b/docs/CLAUDE-DEV-ENVIRONMENT.md index b400703..348a26e 100644 --- a/docs/CLAUDE-DEV-ENVIRONMENT.md +++ b/docs/CLAUDE-DEV-ENVIRONMENT.md @@ -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 @@ -266,7 +267,7 @@ 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 @@ -274,9 +275,12 @@ secret-tool store --label='Claude Code OAuth' \ 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 @@ -284,8 +288,8 @@ Github token scoping and permissions are explained further in **GitHub Token sco 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. diff --git a/docs/CURRENT-STATE.md b/docs/CURRENT-STATE.md index 383442f..ac33273 100644 --- a/docs/CURRENT-STATE.md +++ b/docs/CURRENT-STATE.md @@ -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` --- diff --git a/scripts/claude-dev.env b/scripts/claude-dev.env index 5db7ed7..a510988 100644 --- a/scripts/claude-dev.env +++ b/scripts/claude-dev.env @@ -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] @@ -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=, key=. +# 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 diff --git a/scripts/claude-dev.sh b/scripts/claude-dev.sh index 3499a28..69b5b43 100755 --- a/scripts/claude-dev.sh +++ b/scripts/claude-dev.sh @@ -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 @@ -314,21 +318,22 @@ 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 @@ -336,7 +341,7 @@ case "$CLAUDE_DEV_LLM_BACKEND" in ;; 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}"