# Configuration **Status:** Active **Last Updated:** 2026-07-04 This guide covers storage locations, environment settings, and configuration options for `notebooklm-py`. ## File Locations All data is stored under `~/.notebooklm/` by default, organized by profile: ``` ~/.notebooklm/ ├── config.json # Global config: default_profile, language ├── profiles/ │ ├── default/ # Default profile (auto-created) │ │ ├── storage_state.json # Authentication cookies and session │ │ ├── context.json # CLI context (active notebook, conversation) │ │ └── browser_profile/ # Persistent Chromium profile │ ├── work/ # Named profile example │ │ ├── storage_state.json │ │ ├── context.json │ │ └── browser_profile/ │ └── personal/ │ └── ... ``` `config.json` stores process-wide settings — the persisted default profile name (under the `default_profile` key) and the configured interface language (`language`). It is **global, not per-profile** (see `src/notebooklm/paths.py` and `src/notebooklm/cli/language_cmd.py`). **Legacy layout:** If upgrading from a pre-profile version, the first run auto-migrates flat files into `profiles/default/`. The migration runs under a single-writer `filelock` rooted at `~/.notebooklm/.migration.lock`, so concurrent CLI invocations (e.g., container start-up races) cannot interleave copies — the loser of the lock re-checks the completion marker and no-ops (see `src/notebooklm/migration.py`). The legacy flat layout continues to work as a fallback. You can relocate all files by setting `NOTEBOOKLM_HOME`: ```bash export NOTEBOOKLM_HOME=/custom/path # All files now go to /custom/path/profiles// ``` ### Storage State (`storage_state.json`) Contains the authentication data extracted from your browser session: ```json { "cookies": [ { "name": "SID", "value": "...", "domain": ".google.com", "path": "/", "expires": 1234567890, "httpOnly": true, "secure": true, "sameSite": "Lax" }, ... ], "origins": [], "notebooklm": { "version": 1, "account": { "authuser": 0, "email": "you@example.com" } } } ``` **Cookie requirements** (empirically validated via single- and pair-wise ablation, see `auth-cookie-lifecycle.md` §3.5; enforced by `_validate_required_cookies()` in `auth.py`): - **Tier 1 — strictly required (raises on absence):** `SID` AND `__Secure-1PSIDTS`. `SID` is the only individually-required cookie (`__Secure-1PSIDTS` is removable on its own because Google can re-mint it via `RotateCookies`), but the pair-wise check uncovered that as soon as `__Secure-1PSIDTS` and any one other auth cookie are both missing, Google rejects with `Authentication expired or invalid`. The library therefore enforces both up-front. Authoritative value: `MINIMUM_REQUIRED_COOKIES` in `auth.py`. - **Tier 2 — secondary binding (logs a warning if absent):** either `OSID` is present, or both `APISID` and `SAPISID` are present. Without this, even valid Tier 1 cookies can't authenticate the homepage GET. Logged rather than raised so unverified edge-case flows (e.g. Workspace SSO) aren't broken by a too-strict client check. In practice: extract the full cookie set via `notebooklm login` and don't try to subset it. Partial extractions (a known failure mode of browser-cookies tooling under Chrome 127+ App-Bound Encryption) are the leading suspect for "auth expires immediately" reports — see [#371](https://github.com/teng-lin/notebooklm-py/issues/371). The optional `notebooklm.account` block records the Google account route for multi-account sessions. New `notebooklm login` runs write it when the active account can be discovered safely. Older Playwright-created files can be repaired with `notebooklm auth refresh` when the storage state maps to one visible account; if several accounts are visible, use `notebooklm login --browser-cookies --account EMAIL` to bind the intended account explicitly. `auth refresh` only repairs absent metadata; if metadata exists but points at the wrong account, re-bind explicitly with the browser-cookie login path. **Override location:** ```bash notebooklm --storage /path/to/storage_state.json list ``` ### Context File (`context.json`) Stores the current CLI context, such as the active notebook: ```json { "notebook_id": "abc123def456", "title": "Quarterly review notes", "is_owner": true, "created_at": "2026-05-01T17:43:21Z" } ``` Field summary: - `notebook_id` — currently selected notebook, written by `notebooklm use` and read by every command that takes `-n/--notebook`. - `title`, `is_owner`, `created_at` — optional notebook metadata captured at selection time so `status` / display commands don't need an extra round-trip. Omitted when the CLI didn't have the values to write (see `src/notebooklm/cli/helpers.py:623-651`). This file is managed automatically by `notebooklm use`, `notebooklm clear`, and the `auth` commands. ### Browser Profile (`browser_profile/`) A persistent Chromium user data directory used during `notebooklm login`. **Why persistent?** Google blocks automated login attempts. A persistent profile makes the browser appear as a regular user installation, avoiding bot detection. **To reset:** Delete the `browser_profile/` directory and run `notebooklm login` again. ### Master Token (`master_token.json`) Written only by `notebooklm login --master-token` (the `[headless]` extra). Holds a durable Google master token (mode `0600`) that mints/refreshes the profile's `storage_state.json` cookies with no per-session browser. When present beside a profile's `storage_state.json`, an expired session re-mints from it automatically. ```json {"version": 1, "email": "...", "android_id": "", "master_token": "aas_et/..."} ``` > ⚠️ **Full-account, durable credential** — larger blast radius than > `storage_state.json`; dedicated/throwaway account only. See > [installation.md#alternative-master-token-auth-no-cookie-file-to-ship-survives-expiry](installation.md#d-headless-server-or-ci). ## Environment Variables | Variable | Description | Default | |----------|-------------|---------| | `NOTEBOOKLM_HOME` | Base directory for all files | `~/.notebooklm` | | `NOTEBOOKLM_PROFILE` | Active profile name | `default` | | `NOTEBOOKLM_AUTH_JSON` | Inline authentication JSON (for CI/CD) | - | | `NOTEBOOKLM_NOTEBOOK` | Default notebook ID for commands without `-n/--notebook` | - | | `NOTEBOOKLM_HL` | Default interface/output language code (e.g. `en`, `ja`, `zh_Hans`) | `en` | | `NOTEBOOKLM_BASE_URL` | NotebookLM base URL. Constrained to `https://notebooklm.google.com` (personal) or `https://notebooklm.cloud.google.com` (enterprise) | `https://notebooklm.google.com` | | `NOTEBOOKLM_BL` | `bl` (build label) URL parameter for the chat streaming endpoint; override when chasing a regression tied to a specific frontend build snapshot | built-in default in `_env.DEFAULT_BL` | | `NOTEBOOKLM_TRANSPORT` | HTTP transport backend: `httpx` (default) or `curl_cffi` (opt-in browser-TLS impersonation; requires the `curl_cffi` package). Use `curl_cffi` where the default transport is TLS-fingerprint-blocked. | `httpx` | | `NOTEBOOKLM_LOG_LEVEL` | Logging level: `DEBUG`, `INFO`, `WARNING`, `ERROR` | `WARNING` | | `NOTEBOOKLM_DEBUG_RPC` | Legacy: Enable RPC debug logging (use `LOG_LEVEL=DEBUG` instead) | `false` | | `NOTEBOOKLM_DEBUG` | Show untruncated RPC response bodies in error messages instead of the default 80-char preview (verbose; intended for deep debugging) | `0` | | `NOTEBOOKLM_STRICT_DECODE` | **Retired (ignored since v0.7.0).** Strict decoding is now the only mode: `safe_index` always raises `UnknownRPCMethodError` on schema drift. The former `0` warn-and-fallback opt-out was removed. | (ignored) | | `NOTEBOOKLM_RPC_OVERRIDES` | JSON object mapping `RPCMethod` enum names to RPC ID strings (community self-patch when Google rotates a method ID; e.g. `{"LIST_NOTEBOOKS":"AbC123"}`) | - | | `NOTEBOOKLM_REFRESH_CMD` | Optional command (argv list, or shell string with `_USE_SHELL=1`) invoked when auth refresh is required. Must exit `0` after writing a refreshed `storage_state.json`; the parent reloads from disk | - | | `NOTEBOOKLM_REFRESH_CMD_USE_SHELL` | Opt the `NOTEBOOKLM_REFRESH_CMD` subprocess back into `shell=True` execution. Default `shell=False` (argv list) — set to the literal `1` (only `"1"` is honored — not `true`/`yes`/`on`) when the refresh command requires shell metacharacters | `0` | | `NOTEBOOKLM_REFRESH_PROFILE` | Child-process hint set for `NOTEBOOKLM_REFRESH_CMD`; names the resolved profile being refreshed | resolved profile | | `NOTEBOOKLM_REFRESH_STORAGE_PATH` | Child-process hint set for `NOTEBOOKLM_REFRESH_CMD`; path to the `storage_state.json` file the command must rewrite | resolved storage path | | `NOTEBOOKLM_DISABLE_KEEPALIVE_POKE` | Disable the proactive `accounts.google.com/RotateCookies` poke that refreshes `__Secure-1PSIDTS` ahead of expiry | `0` | | `NOTEBOOKLM_HEADLESS_REAUTH` | Opt in to layer-3 headless re-auth during automatic refresh paths. Explicit `client.refresh_auth(allow_headless=True)` does not require this env var. | `0` | | `NOTEBOOKLM_HEADLESS_REAUTH_CDP_URL` | Optional loopback Chrome DevTools endpoint for layer-3 headless re-auth, e.g. `http://127.0.0.1:9222`. Non-loopback endpoints are ignored for credential safety. | - | | `NOTEBOOKLM_MCP_TRANSPORT` | MCP server transport for `notebooklm-mcp`: `stdio` or `http` | `stdio` | | `NOTEBOOKLM_MCP_HOST` | MCP HTTP transport bind host; non-loopback refused unless `NOTEBOOKLM_MCP_ALLOW_EXTERNAL_BIND=1` | `127.0.0.1` | | `NOTEBOOKLM_MCP_PORT` | MCP HTTP transport bind port | `9420` | | `NOTEBOOKLM_MCP_ALLOW_EXTERNAL_BIND` | Allow MCP HTTP transport to bind a non-loopback host. Use only behind a trusted proxy. | `0` | | `NOTEBOOKLM_MCP_OAUTH_PASSWORD` | Password gating the self-hosted OAuth authorization server that lets claude.ai connect to the remote MCP server (≥16 chars). Set together with `NOTEBOOKLM_MCP_OAUTH_BASE_URL`; both unset → bearer-only. | - | | `NOTEBOOKLM_MCP_OAUTH_BASE_URL` | Bare public HTTPS origin (no path) the self-hosted OAuth endpoints (`/authorize`, `/token`, `/.well-known/*`) mount under. Required with `NOTEBOOKLM_MCP_OAUTH_PASSWORD`; partial/weak/non-HTTPS config refuses to start. | - | | `NOTEBOOKLM_MCP_PUBLIC_URL` | Public base URL for the remote MCP file upload/download signed-URL side-channel (falls back to `NOTEBOOKLM_MCP_OAUTH_BASE_URL`). Unset → `source_add type=file` / `artifact_download` return a "not configured" error. | - | | `NOTEBOOKLM_MCP_TRUST_PROXY` | Trust the proxy-set `CF-Connecting-IP` header as the self-hosted-OAuth login-throttle key. Only enable behind a trusted proxy (e.g. the Cloudflare tunnel); default off keys on the socket peer. | `0` | | `NOTEBOOKLM_MCP_STRICT_IDS` | Strict IDs-only mode for MCP tools: require a full canonical id for every `notebook`/`source`/`note`/`artifact` reference and reject names, titles, and short id prefixes (fail-fast, deterministic automation). | `0` | | `NOTEBOOKLM_SERVER_TOKEN` | Bearer token required by every REST `/v1` request. The REST server refuses to start without it. | - | | `NOTEBOOKLM_SERVER_HOST` | REST server bind host; non-loopback refused unless `NOTEBOOKLM_SERVER_ALLOW_EXTERNAL_BIND=1` | `127.0.0.1` | | `NOTEBOOKLM_SERVER_PORT` | REST server bind port | `8000` | | `NOTEBOOKLM_SERVER_ALLOW_EXTERNAL_BIND` | Allow REST server to bind a non-loopback host. Use only behind a trusted proxy. | `0` | | `NOTEBOOKLM_SERVER_SOURCE_MUTATION_CONCURRENCY` | Max concurrent REST source create/rename/delete/batch handlers. | `4` | | `NOTEBOOKLM_SERVER_SOURCE_WAIT_CONCURRENCY` | Max concurrent REST source wait handlers. | `4` | | `NOTEBOOKLM_SERVER_GENERATION_CONCURRENCY` | Max concurrent REST artifact generation/retry handlers. | `2` | | `NOTEBOOKLM_SERVER_DOWNLOAD_CONCURRENCY` | Max concurrent REST artifact download handlers. | `2` | | `NOTEBOOKLM_SERVER_RESEARCH_CONCURRENCY` | Max concurrent REST research start/cancel/import handlers. | `2` | | `NOTEBOOKLM_SERVER_CHAT_CONCURRENCY` | Max concurrent REST blocking chat ask handlers. | `4` | | `NOTEBOOKLM_QUIET_DEPRECATIONS` | Suppress the project's public-API `DeprecationWarning`s (the one-off warnings routed through `warn_deprecated`, e.g. awaiting `from_storage(...)`). Set to a truthy value (`1` / `true` / `yes` / `on`, case-insensitive) to silence them; see `docs/deprecations.md`. | (warnings emitted) | | `NOTEBOOKLM_FUTURE_ERRORS` | **Retired (removed in v0.8.0; ignored).** It was the v0.7.0 forward-compat preview gate for the v0.8.0 error contract; now that every break it staged is the default, the flag is a no-op — setting it has no effect. See `docs/deprecations.md`. | (ignored) | | `NOTEBOOKLM_VCR_RECORD_ERRORS` | Synthetic-error injection mode for VCR test cassettes (`429`, `5xx`, `expired_csrf`) | - | ### Public config API vs internal resolvers `src/notebooklm/_env.py` owns internal environment/default resolution for runtime behavior. It reads process environment variables and contains internal defaults such as `DEFAULT_BL` for the chat streaming build label. `notebooklm.config` is the stable public import surface. It intentionally re-exports only the supported endpoint/language helpers: `DEFAULT_BASE_URL`, `ENTERPRISE_BASE_HOST`, `get_base_host`, `get_base_url`, `get_default_language`, and `PERSONAL_BASE_HOST`. Existing imports from `notebooklm.config` remain supported; internal-only `_env` names should not be imported by downstream code. ### Env vars and precedence Every `NOTEBOOKLM_*` variable read by the library and CLI, in one place. CLI flags always win over env vars; env vars win over persisted profile config / context; built-in defaults are the last fallback. The "Resolved by" column points at the canonical resolver so the precedence rule for each variable can be audited from one location. | Variable | Purpose | Resolution order (highest → lowest) | Resolved by | |----------|---------|-------------------------------------|-------------| | `NOTEBOOKLM_PROFILE` | Active profile name. Selects which `~/.notebooklm/profiles//` directory backs storage and context. | `-p/--profile` flag → `NOTEBOOKLM_PROFILE` → `default_profile` from `~/.notebooklm/config.json` → `default` | `paths.resolve_profile` | | `NOTEBOOKLM_AUTH_JSON` | Inline `storage_state.json` payload for CI/CD; bypasses on-disk profile storage entirely. | `--storage` flag → `NOTEBOOKLM_AUTH_JSON` → profile-aware `storage_state.json` → legacy fallback | `auth.load_auth_from_storage` | | `NOTEBOOKLM_HOME` | Base directory for all per-profile files. | `NOTEBOOKLM_HOME` → `~/.notebooklm` | `paths.get_home_dir` | | `NOTEBOOKLM_HL` | Default interface/output language for `generate ` and the `hl` query parameter on every batchexecute RPC. | `--language` flag → `NOTEBOOKLM_HL` → `language` value from **global** `~/.notebooklm/config.json` (NOT per-profile) → `en` | `language.resolve_hl` | | `NOTEBOOKLM_LOG_LEVEL` | `DEBUG`/`INFO`/`WARNING`/`ERROR` floor for the `notebooklm` package logger. | `--quiet` flag (forces `ERROR`) → `-v/-vv` flags (force `INFO`/`DEBUG`) → `NOTEBOOKLM_DEBUG_RPC=1` (forces `DEBUG`) → `NOTEBOOKLM_LOG_LEVEL` → `WARNING` | `_logging.configure_logging` + `notebooklm_cli.cli` | | `NOTEBOOKLM_DEBUG_RPC` | Legacy alias that sets the package logger to `DEBUG`. Prefer `NOTEBOOKLM_LOG_LEVEL=DEBUG` for new code. | (See `NOTEBOOKLM_LOG_LEVEL`.) | `_logging.configure_logging` | | `NOTEBOOKLM_NOTEBOOK` | Default notebook ID when no `-n/--notebook` flag is passed. Composes with `notebooklm use ` so per-shell overrides do not clobber the persisted active-notebook context. | `-n/--notebook` flag → `NOTEBOOKLM_NOTEBOOK` → active context (from `notebooklm use`) → error | `cli.helpers.require_notebook` (Click also reads it natively via `cli/options.py:notebook_option`'s `envvar=`) | | `NOTEBOOKLM_RPC_OVERRIDES` | **JSON object** mapping `RPCMethod` enum names to RPC ID strings (e.g. `{"LIST_NOTEBOOKS": "AbC123"}`). Overrides runtime RPC IDs — community self-patch when Google rotates a method ID. Empty string / unset disables the mechanism; invalid JSON or non-object payloads emit a `WARNING` and are ignored. | Process env, evaluated per RPC resolve (cached on the raw env string). | `notebooklm.rpc.overrides._parse_rpc_overrides` | | `NOTEBOOKLM_QUIET_DEPRECATIONS` | Suppress the project's public-API `DeprecationWarning`s — the one-off warnings routed through `src/notebooklm/_deprecation.py::warn_deprecated` (e.g. awaiting `from_storage(...)`). Set to a truthy value (`1` / `true` / `yes` / `on`) to silence them. See `docs/deprecations.md`. | (warnings emitted) | `_deprecation._deprecations_quiet` / `deprecations_quiet` | | `NOTEBOOKLM_FUTURE_ERRORS` | **Retired (removed in v0.8.0; ignored).** It was the v0.7.0 forward-compat preview gate for the v0.8.0 error contract (ADR-0019 / umbrella [#1346](https://github.com/teng-lin/notebooklm-py/issues/1346)). Now that every break it staged — `get()` raising `*NotFoundError`, the attribute-only typed returns, the removed `interval=` alias, the bool→`None` returns, the refusal-raises, and the mutate-existing fail-loud — is the default, the flag is a **no-op**: setting it has no effect. See `docs/deprecations.md`. | (ignored) | — | | `NOTEBOOKLM_STRICT_DECODE` | **Retired (ignored since v0.7.0).** Strict decoding is the only mode — `safe_index` always raises `UnknownRPCMethodError` on schema drift. The former `0` warn-and-fallback opt-out was removed; setting the variable has no effect. | (ignored) | — | | `NOTEBOOKLM_BASE_URL` | NotebookLM base URL. Constrained to `https://notebooklm.google.com` (personal) or `https://notebooklm.cloud.google.com` (enterprise); other schemes/hosts/paths raise `ValueError`. | Process env on every base-URL lookup. | `_env.get_base_url` | | `NOTEBOOKLM_BL` | `bl` (build label) URL parameter sent on the chat streaming endpoint (`ChatAPI.ask`). Pins the frontend build the request is attributed to. | Process env on every chat stream call; whitespace-only falls back to `_env.DEFAULT_BL`. | `_env.get_default_bl` | | `NOTEBOOKLM_DEBUG` | When `1`, RPC error messages include the **full** untruncated response body instead of the default 80-char preview. Verbose; intended for deep debugging only. | Process env on each error formatting call. | `exceptions._truncate_response_preview` | | `NOTEBOOKLM_REFRESH_CMD` | Optional command invoked when auth refresh is required. Must exit `0` after writing a refreshed `storage_state.json`; the parent reloads cookies from disk. Stdout/stderr are not parsed (only surfaced in the non-zero-exit error message). Parsing honors `NOTEBOOKLM_REFRESH_CMD_USE_SHELL`. | Process env on each refresh subprocess spawn. | `auth` refresh-spawn helper (constant `NOTEBOOKLM_REFRESH_CMD_ENV` in `notebooklm.auth`) | | `NOTEBOOKLM_REFRESH_CMD_USE_SHELL` | Opt the optional `NOTEBOOKLM_REFRESH_CMD` subprocess back into `shell=True`. Default `shell=False` parses the command with `shlex.split` and invokes it as an argv list (safer; resists shell-injection footguns when the env var is sourced from CI configs or container env files). | Process env on each refresh subprocess spawn. | `auth` refresh-spawn helper (constant `NOTEBOOKLM_REFRESH_CMD_USE_SHELL_ENV` in `notebooklm.auth`) | | `NOTEBOOKLM_REFRESH_PROFILE` | Child env var injected into `NOTEBOOKLM_REFRESH_CMD`; names the resolved NotebookLM profile that is being refreshed. Refresh scripts may read it, but setting it in the parent shell does not select the profile. | Set by `auth` refresh-spawn helper from the resolved profile. | `auth._run_refresh_cmd` | | `NOTEBOOKLM_REFRESH_STORAGE_PATH` | Child env var injected into `NOTEBOOKLM_REFRESH_CMD`; points to the `storage_state.json` file the command must rewrite before exiting `0`. Refresh scripts may read it, but setting it in the parent shell does not select storage. | Set by `auth` refresh-spawn helper from the explicit storage path or profile-aware storage path. | `auth._run_refresh_cmd` | | `NOTEBOOKLM_DISABLE_KEEPALIVE_POKE` | When `1`, disable the proactive `accounts.google.com/RotateCookies` poke that refreshes `__Secure-1PSIDTS` ahead of expiry. Useful when running behind a proxy that rejects the extra request, or in offline test fixtures. | Process env on every keepalive check. | `auth` keepalive guards (constant `NOTEBOOKLM_DISABLE_KEEPALIVE_POKE_ENV` in `notebooklm.auth`) | | `NOTEBOOKLM_HEADLESS_REAUTH` | Opt in to layer-3 headless re-auth for automatic refresh paths. `client.refresh_auth(allow_headless=True)` is the explicit Python API opt-in and does not require the env var. | Literal `1` enables; all other values disabled. | `_auth.headless_reauth.headless_reauth_env_enabled` | | `NOTEBOOKLM_HEADLESS_REAUTH_CDP_URL` | Optional Chrome DevTools Protocol endpoint for layer-3 headless re-auth. Must be loopback (`127.0.0.1`, `::1`, or `localhost`); remote endpoints are ignored because CDP is account-equivalent. | Explicit function argument → env var → no CDP arm. | `_auth.headless_reauth.resolve_cdp_url` | | `NOTEBOOKLM_MCP_TRANSPORT` | Default transport for `notebooklm-mcp`: `stdio` or `http`. CLI `--transport` wins. | `--transport` flag → env var → `stdio` | `mcp.__main__._build_parser` | | `NOTEBOOKLM_MCP_HOST` | HTTP bind host for `notebooklm-mcp --transport http`. Non-loopback refused unless `NOTEBOOKLM_MCP_ALLOW_EXTERNAL_BIND=1`. | `--host` flag → env var → `127.0.0.1` | `mcp.__main__._build_parser` / `_serving.check_bind_allowed` | | `NOTEBOOKLM_MCP_PORT` | HTTP bind port for `notebooklm-mcp --transport http`. | `--port` flag → env var → `9420` | `mcp.__main__._build_parser` / `_resolve_port` | | `NOTEBOOKLM_MCP_ALLOW_EXTERNAL_BIND` | Allow MCP HTTP transport to bind a non-loopback host. Use only behind a trusted proxy. | Literal `1` enables; all other values disabled. | `mcp.__main__._check_http_bind_allowed` → `_serving.check_bind_allowed` | | `NOTEBOOKLM_MCP_TRUST_PROXY` | Trust the proxy-set `CF-Connecting-IP` header as the self-hosted-OAuth login-throttle key. Enable only behind a trusted proxy (e.g. the Cloudflare tunnel); default off keys the throttle on the socket peer. | Literal `1` enables; all other values disabled. | `mcp._oauth.get_oauth_config` / `_client_ip` | | `NOTEBOOKLM_MCP_STRICT_IDS` | Strict IDs-only mode: MCP `notebook`/`source`/`note`/`artifact` references must be a full canonical id; names, titles, and short id prefixes are rejected before any list call (deterministic automation). Off by default → default name/prefix/title resolution is unchanged. | Literal `1` enables; all other values disabled. | `mcp._resolve._strict_ids_enabled` | | `NOTEBOOKLM_SERVER_TOKEN` | Bearer token required by every REST `/v1` request. The server refuses to start when unset/empty. | `--token` flag → env var → startup failure | `server.__main__._check_token_configured` / `server._auth.require_auth` | | `NOTEBOOKLM_SERVER_HOST` | REST server bind host. Non-loopback refused unless `NOTEBOOKLM_SERVER_ALLOW_EXTERNAL_BIND=1`. | `--host` flag → env var → `127.0.0.1` | `server.__main__._build_parser` / `_serving.check_bind_allowed` | | `NOTEBOOKLM_SERVER_PORT` | REST server bind port. | `--port` flag → env var → `8000` | `server.__main__._build_parser` / `_resolve_port` | | `NOTEBOOKLM_SERVER_ALLOW_EXTERNAL_BIND` | Allow REST server to bind a non-loopback host. Use only behind a trusted proxy. | Literal `1` enables; all other values disabled. | `server.__main__._check_bind_allowed` → `_serving.check_bind_allowed` | | `NOTEBOOKLM_SERVER_SOURCE_MUTATION_CONCURRENCY` | Max concurrent REST source create/rename/delete/batch handlers. | Env var → `4`; blank uses default; must be integer `>=1`. | `server._limits.ServerLimiters.from_env` | | `NOTEBOOKLM_SERVER_SOURCE_WAIT_CONCURRENCY` | Max concurrent REST source wait handlers. | Env var → `4`; blank uses default; must be integer `>=1`. | `server._limits.ServerLimiters.from_env` | | `NOTEBOOKLM_SERVER_GENERATION_CONCURRENCY` | Max concurrent REST artifact generation/retry handlers. | Env var → `2`; blank uses default; must be integer `>=1`. | `server._limits.ServerLimiters.from_env` | | `NOTEBOOKLM_SERVER_DOWNLOAD_CONCURRENCY` | Max concurrent REST artifact download handlers. | Env var → `2`; blank uses default; must be integer `>=1`. | `server._limits.ServerLimiters.from_env` | | `NOTEBOOKLM_SERVER_RESEARCH_CONCURRENCY` | Max concurrent REST research start/cancel/import handlers. | Env var → `2`; blank uses default; must be integer `>=1`. | `server._limits.ServerLimiters.from_env` | | `NOTEBOOKLM_SERVER_CHAT_CONCURRENCY` | Max concurrent REST blocking chat ask handlers. | Env var → `4`; blank uses default; must be integer `>=1`. | `server._limits.ServerLimiters.from_env` | | `NOTEBOOKLM_VCR_RECORD_ERRORS` | Synthetic-error injection mode for VCR test cassettes. Lowercase-normalized; valid values are `429` (rate limit), `5xx` (server error), or `expired_csrf` (CSRF token expiration). Used to record synthetic error cassettes under VCR. | Process env on each request, evaluated by `ErrorInjectionMiddleware` to intercept and synthesize failures. | `_error_injection._get_error_injection_mode` | **Boolean handling.** `NOTEBOOKLM_DEBUG_RPC` treats `1` / `true` / `yes` (case-insensitive) as truthy; everything else is falsy. `NOTEBOOKLM_STRICT_DECODE` is ignored as of v0.7.0 (strict decoding is the only mode); no value changes decoder behavior. `NOTEBOOKLM_QUIET_DEPRECATIONS` treats `1` / `true` / `yes` / `on` (case-insensitive) as truthy; any other value (including unset) leaves deprecation warnings enabled. It silences the project's public-API `DeprecationWarning`s — the `get()`-returns-`None` warning and deprecated keyword aliases (e.g. `ResearchAPI.wait_for_completion`'s legacy `interval=`) — without changing their behavior. `NOTEBOOKLM_NOTEBOOK` is treated as unset when empty or whitespace-only so a bare `export NOTEBOOKLM_NOTEBOOK=` does not block `notebooklm use` / `-n/--notebook` from resolving. **The `--quiet` global flag.** `notebooklm --quiet ` suppresses status prose and raises the `notebooklm` package logger floor to `ERROR` for the duration of one invocation, so cron and CI logs stay clean while real failures still surface. Structured `--json` payloads are still emitted. It is mutually exclusive with `-v/-vv` — combining the two raises a `UsageError` (exit `2`) since the resolved log levels conflict (`ERROR` vs `INFO`/`DEBUG`). For shell-wide / always-on log suppression, use `NOTEBOOKLM_LOG_LEVEL`. ### NOTEBOOKLM_HOME Relocates all configuration files to a custom directory: ```bash export NOTEBOOKLM_HOME=/custom/path # All files now go here: # /custom/path/profiles//storage_state.json # /custom/path/profiles//context.json # /custom/path/profiles//browser_profile/ ``` **Use cases:** - Per-project isolation - Custom storage locations ### NOTEBOOKLM_PROFILE Selects the active profile without changing the persisted default: ```bash export NOTEBOOKLM_PROFILE=work notebooklm list # Uses ~/.notebooklm/profiles/work/ ``` Equivalent to passing `-p work` on every command. The CLI flag takes precedence over the env var. The **persisted default** is read from the `default_profile` key of `~/.notebooklm/config.json` (set via `notebooklm profile switch `). When neither a `-p/--profile` flag nor `NOTEBOOKLM_PROFILE` is set, `paths.resolve_profile` falls back to this value (and finally to `"default"` if `config.json` doesn't exist or has no `default_profile` key). ### NOTEBOOKLM_AUTH_JSON Provides authentication inline without writing files. Ideal for CI/CD: ```bash export NOTEBOOKLM_AUTH_JSON='{"cookies":[...]}' notebooklm list # Works without any file on disk ``` **Precedence:** 1. `--storage` CLI flag (highest) 2. `NOTEBOOKLM_AUTH_JSON` environment variable 3. Profile-aware path: `$NOTEBOOKLM_HOME/profiles//storage_state.json` 4. `~/.notebooklm/profiles/default/storage_state.json` (default) 5. `~/.notebooklm/storage_state.json` (legacy fallback) **Note:** Cannot run `notebooklm login` when `NOTEBOOKLM_AUTH_JSON` is set. ### `NOTEBOOKLM_REFRESH_CMD` Optional. If set, this command is invoked when an auth refresh is required — replacing the default browser-cookie path. The contract is **exit-code based**: 1. The command must exit `0`. 2. On exit, it must have written a refreshed `storage_state.json` at the path in `NOTEBOOKLM_REFRESH_STORAGE_PATH`. The parent sets this child env var to the explicit storage path or the resolved profile-aware storage path before spawning the command. The parent then reloads cookies from disk and retries the token fetch. Stdout and stderr are **not** parsed — they are only captured for inclusion in the error message when the command exits non-zero. Read by the `NOTEBOOKLM_REFRESH_CMD_ENV` constant in `notebooklm.auth`. The child process also receives `NOTEBOOKLM_REFRESH_PROFILE` with the resolved profile name. See also `NOTEBOOKLM_REFRESH_CMD_USE_SHELL` to opt back into `shell=True` parsing. ### NOTEBOOKLM_HL Sets the default interface/output language used by the client. The value is passed as the `hl` query parameter on every batchexecute RPC call and is used by the `generate audio|video|slide-deck|infographic|data-table|mind-map|report` commands. In the Python API, omitted `language` on `ArtifactsAPI.generate_*` keeps the historical `"en"` artifact default; pass `language=None` to opt in to this `NOTEBOOKLM_HL` resolver. ```bash export NOTEBOOKLM_HL=ja notebooklm generate audio "deep dive" # Japanese audio overview ``` Surrounding whitespace is stripped; an empty or whitespace-only value falls back to `en`. For the generate commands, the resolution order is: 1. `--language` CLI flag 2. `NOTEBOOKLM_HL` environment variable 3. `language` value from the **global** `~/.notebooklm/config.json` (set via `notebooklm language set `). The language is stored once per `NOTEBOOKLM_HOME`, **not** per profile — switching `notebooklm -p work` does not switch the configured language. See `src/notebooklm/cli/language_cmd.py` for the resolver and `src/notebooklm/paths.py` for the storage location. 4. `en` (built-in default) ### NOTEBOOKLM_QUIET_DEPRECATIONS Suppresses the project's public-API `DeprecationWarning`s while you migrate. Set it to a truthy value (`1`, `true`, `yes`, or `on`, case-insensitive) to silence them; any other value (including unset) leaves them enabled. It gates the one-off deprecation warnings routed through `src/notebooklm/_deprecation.py::warn_deprecated` — e.g. awaiting `NotebookLMClient.from_storage(...)` instead of using the `async with` form. (The v0.7.0 error-contract runways it also gated — the `get()`-returns-`None` warning, the `wait_for_completion(interval=...)` alias, and the dict-subscript bridge — all **completed their removal in v0.8.0**, so those warnings no longer exist; see [`deprecations.md`](deprecations.md).) The helper that reads this variable is `notebooklm._deprecation._deprecations_quiet` (public alias `deprecations_quiet`). ```bash # Silence the project's public-API deprecation warnings while migrating export NOTEBOOKLM_QUIET_DEPRECATIONS=1 ``` > Note: this variable does **not** affect `source add --mime-type` / > `client.sources.add_file(mime_type=...)` — `mime_type` is a supported > parameter and emits no warning. ### NOTEBOOKLM_FUTURE_ERRORS (removed in v0.8.0) **Removed.** This was the v0.7.0 forward-compat preview gate for the v0.8.0 error contract (ADR-0019 / umbrella [#1346](https://github.com/teng-lin/notebooklm-py/issues/1346)): setting it made the v0.7.0 warn-runways adopt their v0.8.0 raise-target early so you could test forward-compatibility before the breaking flips shipped. v0.8.0 makes every one of those flips the default — `get()` raising `*NotFoundError` on a miss, the attribute-only typed returns, the removed `interval=` alias, the bool→`None` returns, the refusal-raises, and the mutate-existing fail-loud — so the flag and its resolver were deleted. **Setting `NOTEBOOKLM_FUTURE_ERRORS` now has no effect.** Remove it from your environment / CI config. See [`deprecations.md`](deprecations.md) for the full Removed-in-v0.8.0 table. ### Timeouts Most batchexecute RPCs issued by the client (whether through `NotebookLMClient` or any of the CLI commands) use a **30-second** HTTP request timeout by default, with a tighter **10-second** connection-establishment timeout. The shorter connect timeout helps surface network-level issues quickly while the read timeout accommodates slow server responses. The timeout is exposed as a constructor argument on `NotebookLMClient` (`timeout=`) for callers that need to tune it per-workload — see the `DEFAULT_TIMEOUT` / `DEFAULT_CONNECT_TIMEOUT` constants in `src/notebooklm/_runtime/config.py`. The chat streaming endpoint (`ChatAPI.ask`) also exposes a separate per-read silence window (`chat_timeout=`). It defaults to **180 seconds** because shared notebooks can be slow to send the first streamed chat byte; fast metadata RPCs stay on the normal **30-second** timeout. A chat read timeout means the server sent no stream bytes for that window, either before the first byte or between chunks; it does not mean total generation time exceeded 30 seconds. Pass `chat_timeout=None` to inherit the normal client timeout for chat. The CLI `ask --request-timeout N` flag overrides both values for that invocation. ### Decoder strictness NotebookLM's batchexecute responses are obfuscated, undocumented, and reshaped by Google without notice. The decoder uses a shared `safe_index` helper to walk nested response payloads. When it can't descend (an index is out of range, or the value at a step isn't indexable), it **raises** `UnknownRPCMethodError` (a subclass of `DecodingError` / `RPCError`) with structured `method_id`, `path`, `source`, and `data_at_failure` attributes. Strict decoding is the only mode. The legacy `NOTEBOOKLM_STRICT_DECODE=0` warn-and-return-`None` opt-out (which emitted a `DeprecationWarning` through v0.5.0/v0.6.0) was **retired in v0.7.0**; the env var is now ignored. The strict contract surfaces real schema drift (Google rotating a response shape) as a typed exception instead of a silent `None` return, so callers that previously treated `None` as a sentinel must handle `UnknownRPCMethodError`. The same `UnknownRPCMethodError` is also raised by `decode_response()` when the batchexecute response contains RPC IDs but not the one the call requested (typically a sign that Google rotated the method ID). > Background and rationale for the flip: see > [`docs/adr/0011-schema-validation-policy.md`](adr/0011-schema-validation-policy.md). ## CLI Options ### Global Options | Option | Description | Default | |--------|-------------|---------| | `--storage PATH` | Path to storage_state.json | `$NOTEBOOKLM_HOME/profiles//storage_state.json` | | `-p, --profile NAME` | Use a named profile | Active profile or `default` | | `-v, --verbose` | Enable verbose output (`-v` for INFO, `-vv` for DEBUG) | - | | `--quiet` | Suppress status output and INFO/WARN logs (only errors survive). Mutually exclusive with `-v`. | - | | `--version` | Show version | - | | `--help` | Show help | - | ### Viewing Configuration See where your configuration files are located: ```bash notebooklm status --paths ``` Output: ``` Configuration Paths ┏━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━┓ ┃ File ┃ Path ┃ Source ┃ ┡━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━┩ │ Profile │ default │ active │ │ Home Directory │ /home/user/.notebooklm │ default │ │ Storage State │ .../profiles/default/storage_state.json │ │ │ Context │ .../profiles/default/context.json │ │ │ Browser Profile │ .../profiles/default/browser_profile │ │ └─────────────────┴──────────────────────────────────────────┴───────────┘ ``` ## Session Management ### Session Lifetime Authentication sessions are tied to Google's cookie expiration: - Sessions typically last several days to weeks - Google may invalidate sessions for security reasons - Rate limiting or suspicious activity can trigger earlier expiration ### Refreshing Sessions **Automatic Refresh:** CSRF tokens and session IDs are automatically refreshed when authentication errors are detected. This handles most "session expired" errors transparently. **Manual Re-authentication:** If your session cookies have fully expired (automatic refresh won't help), re-authenticate: ```bash notebooklm login ``` ### Multiple Accounts **Profiles (recommended):** Use named profiles to manage multiple Google accounts under a single home directory: ```bash # Create and authenticate profiles notebooklm profile create work notebooklm -p work login notebooklm -p work list notebooklm profile create personal notebooklm -p personal login notebooklm -p personal list # Switch the active profile notebooklm profile switch work notebooklm list # Uses work profile # List all profiles notebooklm profile list # Use env var for session-wide override export NOTEBOOKLM_PROFILE=personal notebooklm list # Uses personal profile ``` Each profile stores its own `storage_state.json`, `context.json`, and `browser_profile/` under `~/.notebooklm/profiles//`. **Alternative: `NOTEBOOKLM_HOME`** still works for full directory-level isolation: ```bash export NOTEBOOKLM_HOME=~/.notebooklm-work notebooklm login ``` **One-off override with `--storage`:** ```bash notebooklm --storage /path/to/storage_state.json list ``` When `--storage ` is set, **two different context files** are used — they are NOT the same file: - **Notebook / conversation context** lives at a *suffixed* file `.context.json` (`storage_path.with_suffix(storage_path.suffix + ".context.json")`, see `paths.get_context_path`). Two `--storage` invocations against different files cannot see each other's selected notebook, and neither pollutes the default profile context. - **Account-routing metadata** (the `notebooklm.account` object — `authuser` index and optional `email`) lives in-band inside the selected `storage_state.json`. This keeps copied files and `NOTEBOOKLM_AUTH_JSON` secrets bound to the same Google account route as the original profile. Run `notebooklm --storage status --paths` to see exactly which context file is being used for notebook selection. ## CI/CD Configuration ### GitHub Actions (Recommended) Use `NOTEBOOKLM_AUTH_JSON` for secure, file-free authentication: ```yaml jobs: notebook-task: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Setup Python uses: actions/setup-python@v5 with: python-version: '3.12' - name: Install notebooklm-py run: pip install notebooklm-py # Pre-flight: fail fast and loud on missing/expired auth. # `auth check --json` returns exit 0 even when status is "error"; --test makes the network # call needed to detect expired cookies, and the `jq -e` flag converts a non-"ok" status # into a non-zero exit code so the runner step actually fails. - name: Verify auth (fail-fast on expired cookies) env: NOTEBOOKLM_AUTH_JSON: ${{ secrets.NOTEBOOKLM_AUTH_JSON }} run: notebooklm auth check --test --json | jq -e '.status == "ok"' - name: List notebooks env: NOTEBOOKLM_AUTH_JSON: ${{ secrets.NOTEBOOKLM_AUTH_JSON }} run: notebooklm list ``` **Benefits:** - No file writes needed - Secret stays in memory only - Clean, simple workflow ### Obtaining the Secret Value 1. Run `notebooklm login` locally 2. If the file was created by an older Playwright login and lacks `notebooklm.account`, run `notebooklm auth refresh` to repair single-account states or re-login with `notebooklm login --browser-cookies --account EMAIL` for multi-account states 3. Copy the contents of `~/.notebooklm/profiles/default/storage_state.json` (the canonical write location; the legacy `~/.notebooklm/storage_state.json` is only read as a fallback) 4. Add as a GitHub repository secret named `NOTEBOOKLM_AUTH_JSON` (see [installation.md#d-headless-server-or-ci](installation.md#d-headless-server-or-ci) for trailing-newline + ephemeral-runner refresh notes) ### Alternative: File-Based Auth If you prefer file-based authentication: ```yaml - name: Setup NotebookLM auth run: | mkdir -p ~/.notebooklm/profiles/default echo "${{ secrets.NOTEBOOKLM_AUTH_JSON }}" > ~/.notebooklm/profiles/default/storage_state.json chmod 600 ~/.notebooklm/profiles/default/storage_state.json - name: List notebooks run: notebooklm list ``` For profile-specific CI auth: ```yaml - name: Setup work profile auth run: | mkdir -p ~/.notebooklm/profiles/work echo "${{ secrets.WORK_AUTH_JSON }}" > ~/.notebooklm/profiles/work/storage_state.json chmod 600 ~/.notebooklm/profiles/work/storage_state.json - name: List notebooks (work) run: notebooklm -p work list ``` ### Session Expiration CSRF tokens are automatically refreshed during API calls. However, the underlying session cookies still expire. For long-running CI pipelines: - Update the `NOTEBOOKLM_AUTH_JSON` secret every 1-2 weeks - Monitor for persistent auth failures (these indicate cookie expiration) ## Debugging ### Enable Verbose Output Some commands support verbose output via Rich console: ```bash # Most errors are printed to stderr with details notebooklm list 2>&1 | cat ``` ### Enable RPC Debug Logging ```bash NOTEBOOKLM_DEBUG_RPC=1 notebooklm list ``` ### Logger namespace compatibility Runtime transport and middleware logs still use the historical `notebooklm._core` logger key via `CORE_LOGGER_NAME`. That name is a compatibility logging contract for existing filters and tests; it does not mean the deleted `_core.py` module or a concrete `Session` owner still exists. ### Check Authentication Verify your session is working: ```bash # Should list notebooks or show empty list notebooklm list # If you see "Unauthorized" or redirect errors, re-login notebooklm login ``` ### Check Configuration Paths ```bash # See where files are being read from notebooklm status --paths ``` ### Network Issues The CLI uses `httpx` for HTTP requests. Common issues: - **Timeout**: Google's API can be slow; large operations may time out - **SSL errors**: Ensure your system certificates are up to date - **Proxy**: Set standard environment variables (`HTTP_PROXY`, `HTTPS_PROXY`) if needed ## Platform Notes ### macOS Works out of the box. Chromium is downloaded automatically by Playwright. ### Linux For Playwright system dependencies and the Chromium install on Debian/Ubuntu, see [docs/installation.md#platform-notes](installation.md#platform-notes) (and [troubleshooting.md#linux](troubleshooting.md#linux) if you hit `TypeError: onExit is not a function`). ### Windows Works with PowerShell or CMD. Use backslashes for paths: ```powershell notebooklm --storage C:\Users\Name\.notebooklm\storage_state.json list ``` Or set environment variable: ```powershell $env:NOTEBOOKLM_HOME = "C:\Users\Name\custom-notebooklm" notebooklm list ``` ### WSL Browser login opens in the Windows host browser. The storage file is saved in the WSL filesystem. ### Headless Servers & Containers **Playwright is only required for the `notebooklm login` command.** All other operations use standard HTTP requests via `httpx`. For the install + auth-bootstrap recipe (run `notebooklm login` on a workstation, copy `storage_state.json` to the server, set `NOTEBOOKLM_AUTH_JSON`), see the canonical Persona D guide: [docs/installation.md#d-headless-server-or-ci](installation.md#d-headless-server-or-ci).