Skip to content
Closed
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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ Run `colab <command> --help` to view specific options, defaults, and detailed he
| `colab update [--install]` | Check for a newer release (and optionally upgrade the CLI in place) |

### Global Options
* `--auth {oauth2,adc}` — Authentication strategy for the Colab API (default: `adc`).
* `--auth {oauth2,adc}` — Authentication strategy for the Colab API (default: `oauth2`).
* `-c, --client-oauth-config PATH` — Path to public OAuth client credentials configuration (default: `~/.colab-cli-oauth-config.json`).
* `--config PATH` — Path to local session metadata storage (default: `~/.config/colab-cli/sessions.json`).
* `--logtostderr` — Direct debug logging output to stderr.
Expand Down
3 changes: 2 additions & 1 deletion docs/01_session_management.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
---
log:
2026-06-19: Synced the implementation details with the current auth architecture. The default OAuth2 provider now uses the remote copy-paste flow, and ADC remains available via `--auth=adc`; the old local-server OAuth wording was stale after the 2026-06-11 automation/auth update.
2026-06-15: Switched the keep-alive daemon from the `colab.pa.googleapis.com` `RuntimeService/KeepAliveAssignment` RPC to a Tunnel Frontend HTTP ping (`GET /tun/m/<endpoint>/keep-alive/` with `X-Colab-Tunnel: Google`) on `colab.research.google.com`. The RPC required `serviceusage` consumer access to Colab's internal project `1014160490159`, which ordinary user accounts lack, so every external user hit HTTP 403 `USER_PROJECT_DENIED` and their CLI sessions were idle-pruned within minutes (issue #14). Reproduced live with a third-party account; verified the tunnel ping is accepted by the same bearer-token credential that already works for `assign`. A `ReadTimeout` on the ping is treated as success (TFE records activity before forwarding to the often-non-responding VM). Generalized the pre-flight remediation messaging away from the now-irrelevant `colaboratory`/`pa.googleapis.com` framing, and removed the dead grpc-web client-registry/API-key code.
2026-06-10: Replaced the POSIX-only `fcntl.flock` file locking in `_LockedFileStore` with the cross-platform `filelock` library (reported broken on Windows). Reads use `ReadWriteLock.read_lock()` (shared) and writes use `write_lock()` (exclusive), preserving the original `LOCK_SH`/`LOCK_EX` semantics. The lock is constructed with `is_singleton=False` so two `StateStore` instances for the same path in one process don't collapse into a single reentrant lock (which would raise `RuntimeError` on multi-threaded write contention). Added shared-read, cross-process exclusion, and multi-thread/multi-process regression tests.
---
Expand Down Expand Up @@ -87,7 +88,7 @@ To prevent Colab VMs from being deleted due to idle timeouts (standard is ~90 mi
- **Resource Usage**: Add real-time resource usage (CPU/RAM/GPU) to the `status` output by executing a diagnostic snippet on the VM.

## Implementation Details
- **Authentication**: Uses `google-auth-oauthlib` to perform a local server OAuth flow.
- **Authentication**: Uses `google-auth-oauthlib` for the default OAuth2 remote copy-paste flow, or Application Default Credentials when `--auth=adc` is selected.
- **Global Flags**:
- `-c`, `--client-oauth-config`: Path to the client secrets JSON file (default: `~/.colab-cli-oauth-config.json`).
- `--config`: Path to the session state JSON file (default: `~/.config/colab-cli/sessions.json`).
Expand Down
14 changes: 7 additions & 7 deletions skills/colab-operator/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,19 +26,19 @@ by running `uv tool install google-colab-cli` or `pip install google-colab-cli`.
- **`colab` is fire-and-forget.** Each command authenticates, does one thing, and exits. A detached background daemon (spawned by `colab new`) handles keep-alive; you don't manage it.

## Authentication (the #1 thing that blocks agents)
- The global flag is `--auth={adc,oauth2}` and the **default is `adc`** (Application Default Credentials). It must come *before* the subcommand: `colab --auth=adc new -s x`.
- **ADC setup** (most reliable for headless/agent use). The Colab backends need a specific scope set, so re-mint ADC with all four scopes:
- The global flag is `--auth={oauth2,adc}` and the **default is `oauth2`**. It must come *before* the subcommand: `colab --auth=adc new -s x`.
- **ADC setup** (most reliable for headless/agent use when ambient Google credentials are preferred). The Colab backends need a specific scope set, so re-mint ADC with all four scopes:
```bash
gcloud auth application-default login \
--scopes=openid,\
https://www.googleapis.com/auth/cloud-platform,\
https://www.googleapis.com/auth/userinfo.email,\
https://www.googleapis.com/auth/colaboratory
```
Why all four: `userinfo.email` (session backend `colab.research.google.com`, else 401), `colaboratory` (RuntimeService `colab.pa.googleapis.com` keep-alive, else 403), `openid`+`cloud-platform` (mandated by gcloud itself; it rejects scope lists missing `cloud-platform`).
- **oauth2 setup**: `colab --auth=oauth2 <anything>` triggers a browser consent flow on first use (token cached at `~/.config/colab-cli/token.json`). Requires a client config at `~/.colab-cli-oauth-config.json` (or `-c PATH`). The browser step means it usually needs a human; prefer ADC for agents.
- **Verify auth in one shot**: `colab sessions` (read-only, lists server assignments) or `colab whoami` (hidden debug command: prints the active email, scopes, audience, and expiry). When any call 403s against `colab.pa.googleapis.com`, the cause is almost always a missing scope — `colab whoami` shows it instantly.
- **`colab new` pre-flights the keep-alive RPC** right after allocating. If your token lacks the `colaboratory` scope it unassigns the fresh VM (so you don't leak a billable assignment) and prints the exact remediation. Follow that message rather than retrying blindly.
Why all four: `userinfo.email` is required by the session backend at `colab.research.google.com`; `colaboratory` is retained for forward compatibility and other Colab features; `openid`+`cloud-platform` are mandated by `gcloud` itself, which rejects scope lists missing `cloud-platform`.
- **oauth2 setup**: `colab --auth=oauth2 <anything>` triggers a remote copy-paste browser consent flow on first use (token cached at `~/.config/colab-cli/token.json`). If no explicit `-c PATH` or `~/.colab-cli-oauth-config.json` is present, the CLI falls back to its bundled OAuth config. The browser/code step means it usually needs a human; prefer ADC for unattended agents.
- **Verify auth in one shot**: `colab sessions` (read-only, lists server assignments) or `colab whoami` (hidden debug command: prints the active email, scopes, audience, and expiry). When a session operation fails with 401/403, first verify identity and scopes rather than retrying blindly.
- **`colab new` pre-flights keep-alive** right after allocating. Keep-alive now uses the same `colab.research.google.com` tunnel credential path as assignment, so scope failures should normally surface at assignment time; if pre-flight does fail with a scope error, the CLI unassigns the fresh VM (so you don't leak a billable assignment) and prints provider-specific remediation.
- **Do NOT confuse `colab auth` with CLI authentication.** `colab auth` injects *VM-side* GCP credentials into the running kernel (so notebook code can call BigQuery/GCS); it is orthogonal to how the CLI itself authenticates. Never suggest "run `colab auth`" to fix a CLI 401/403 — that's a scope/identity problem fixed via the `gcloud` command above.

## Workflow
Expand Down Expand Up @@ -86,4 +86,4 @@ by running `uv tool install google-colab-cli` or `pip install google-colab-cli`.
## Recovery
- "Session not found" / 404 / 401 on exec: the backend pruned the VM. `colab exec`/`repl` detect this and clean up local state automatically — run `colab sessions` and re-create with `colab new`.
- Execution timeout or wedged kernel: `colab restart-kernel -s <name>` (keeps the VM, resets the kernel), or `colab stop` then `colab new`.
- Keep-alive daemon died (`colab log` shows `keep_alive_stopped reason=consecutive_4xx_errors`): almost always the missing `colaboratory` scope — re-auth per the Authentication section.
- Keep-alive daemon died (`colab log` shows `keep_alive_stopped reason=consecutive_4xx_errors`): inspect the logged status/body. A real 4xx usually means the assignment expired or credentials are wrong; read-timeouts on the tunnel keep-alive path are treated as success.
11 changes: 11 additions & 0 deletions tests/test_readme.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.

from pathlib import Path
from unittest.mock import patch, mock_open, MagicMock
from typer.testing import CliRunner
import pytest
Expand Down Expand Up @@ -90,3 +91,13 @@ def test_readme_failure(mock_resources):
result = runner.invoke(app, ["README"])
assert result.exit_code == 1
assert "README.md content not available" in result.output


def test_auth_docs_match_current_cli_default():
repo_root = Path(__file__).resolve().parents[1]
readme = (repo_root / "README.md").read_text()
skill = (repo_root / "skills" / "colab-operator" / "SKILL.md").read_text()

assert "default: `oauth2`" in readme
assert "default is `oauth2`" in skill
assert "RuntimeService `colab.pa.googleapis.com` keep-alive" not in skill