Celestiorax/dante-notebooklm-runner
DANTE NotebookLM Runner
Runner externo Python/Docker para conectar o DANTE ao NotebookLM usando notebooklm-py.
O Worker do Cloudflare continua sendo apenas a ponte segura. Ele valida CORS, rate limit, status, gates e sanitizacao; este runner fica fora do frontend e guarda a sessao Google/NotebookLM como secret backend-only.
Por que existe
notebooklm-py e qualquer fluxo com Python, cookies Google, storage_state.json ou Playwright nao cabem com seguranca dentro do Cloudflare Worker. O desenho correto e:
DANTE UI/Worker -> /api/notebooklm/* -> runner HTTPS -> notebooklm-py -> NotebookLM
Contrato HTTP
GET /healthGET /auth/statusGET /auth/checkGET /auth/diagnosticsGET /auth/login-instructionsPOST /auth/refreshGET /notebooksPOST /notebooks/listPOST /notebooks/createPOST /notebooks/usePOST /sources/add-urlPOST /sources/add-textPOST /sources/add-filePOST /sources/addPOST /sources/sync-manifestPOST /chat/askPOST /artifacts/generateGET /artifacts/{id}/statusGET /artifacts/{id}/downloadPOST /artifacts/download/metadata
GET /health e publico e retorna apenas o contrato seguro de readiness:
{"ok": true, "status": "ready", "ready": true, "healthy": true, "runner": "notebooklm_runner", "degraded": false}Todos os demais endpoints exigem Authorization: Bearer NOTEBOOKLM_RUNNER_TOKEN.
Setup local
python -m venv .venv
. .venv/bin/activate
pip install -r requirements.txt
cp .env.example .env
uvicorn app.main:app --host 0.0.0.0 --port 8788Docker
docker build -t dante-notebooklm-runner .
docker run --env-file .env -p 8788:8788 dante-notebooklm-runnerAutenticacao NotebookLM no Hugging Face
Use uma conta Google dedicada. Gere storage_state.json ou NOTEBOOKLM_AUTH_JSON no host do runner e monte como secret/volume. Nunca copie essa sessao para Git, Worker, frontend, log publico ou prompt.
O Hugging Face Space deve ter storage persistente anexado em /data, read-write. Sem esse mount, o Space pode ate ficar online, mas a sessao Google volta a expirar ou desaparecer entre rebuilds/restarts.
Configure estas Variables no Space:
NOTEBOOKLM_HOME=/data/.notebooklmNOTEBOOKLM_PROFILE=defaultNOTEBOOKLM_STORAGE_STATE_PATH=/data/.notebooklm/profiles/default/storage_state.jsonNOTEBOOKLM_DEFAULT_NOTEBOOK_ID=2315989e-2333-4639-8442-e52fd3005768NOTEBOOKLM_RUNNER_REQUIRE_AUTH=trueNOTEBOOKLM_HL=pt-BRNOTEBOOKLM_BASE_URL=https://notebooklm.google.comNOTEBOOKLM_REAL_WRITE_ENABLED=falseNOTEBOOKLM_REAL_ARTIFACT_GENERATION_ENABLED=false
Configure estes Secrets no Space:
NOTEBOOKLM_RUNNER_TOKENNOTEBOOKLM_AUTH_JSON, opcional e apenas como bootstrap/fallback
Prioridade de auth:
NOTEBOOKLM_STORAGE_STATE_PATH, se o arquivo existe e tem tamanho maior que zero.NOTEBOOKLM_HOME/profiles/<profile>/storage_state.json.NOTEBOOKLM_AUTH_JSONcomo bootstrap backend-only; o runner valida o JSON, grava nostorage_state.jsonresolvido e passa a preferir o arquivo persistente.- Sem storage e sem bootstrap, o runner retorna
missing_autheneeds_login=true.
/auth/status nao prova login real. Ele mostra estado de storage e a ultima validacao conhecida. Somente /auth/check chama NotebookLM de verdade com client.notebooks.list() e pode retornar ok=true.
Interpretacao segura:
auth_json=present_backend_only: existe secret de bootstrap no backend; isso nao significa login valido.storage_state=generated_from_auth_json: o bootstrap foi gravado no path persistente.storage_state=present: existe arquivo de sessao; ainda precisa passar em/auth/check.status=auth_expired_or_invalid: NotebookLM redirecionou para login ou rejeitou a sessao.persistent_storage_detected=false: anexar storage persistente em/dataou corrigir permissoes.
Quando a sessao expirar, o DANTE deve mostrar NotebookLM precisa reautenticar e apontar para este runbook; nunca deve pedir senha Google na UI. Reautentique pelo terminal/Dev Mode do Space se o fluxo estiver disponivel, ou substitua o Secret NOTEBOOKLM_AUTH_JSON por um bootstrap novo e faca rebuild. Nunca retorne, copie para log ou comite cookies, tokens, JSON de auth ou storage_state.json.
Adapter real
O runner usa NotebookLMClient.from_storage() da biblioteca pinada notebooklm-py e faz binding defensivo dos metodos publicos reais:
client.notebooks.list()client.notebooks.create(title)client.notebooks.get(notebook_id)client.sources.add_url(notebook_id, url)client.sources.add_text(notebook_id, title, content)client.chat.ask(notebook_id, prompt, ...)client.artifacts.generate_*quandoartifact_typee suportado
Reads podem executar com auth valido. Writes reais retornam status=blocked, reason=real_write_disabled, executed=false ate NOTEBOOKLM_REAL_WRITE_ENABLED=true. Artefatos reais ficam bloqueados ate NOTEBOOKLM_REAL_ARTIFACT_GENERATION_ENABLED=true.
Se a API do notebooklm-py mudar, o runner retorna status=adapter_error ou status=unsupported com mensagem sanitizada, sem traceback publico.
Testes
Local:
python -m compileall app
python -m pytestProducao/Hugging Face, em PowerShell:
$base = "https://SEU-SPACE.hf.space"
$headers = @{ Authorization = "Bearer $env:NOTEBOOKLM_RUNNER_TOKEN" }
Invoke-RestMethod "$base/health" -Headers $headers
Invoke-RestMethod "$base/auth/status" -Headers $headers
Invoke-RestMethod "$base/auth/diagnostics" -Headers $headers
Invoke-RestMethod "$base/auth/check" -Headers $headers
Invoke-RestMethod "$base/auth/login-instructions" -Headers $headers
Invoke-RestMethod "$base/notebooks" -Headers $headers
Invoke-RestMethod "$base/notebooks/list" -Headers $headers -Method Post -Body "{}" -ContentType "application/json"
Invoke-RestMethod "$base/notebooks/create" -Headers $headers -Method Post -Body '{"title":"DANTE smoke","dry_run":false}' -ContentType "application/json"
Invoke-RestMethod "$base/sources/add-text" -Headers $headers -Method Post -Body '{"notebook_id":"NOTEBOOK_ID","title":"Smoke.md","text":"Teste DANTE","dry_run":false}' -ContentType "application/json"
Invoke-RestMethod "$base/chat/ask" -Headers $headers -Method Post -Body '{"notebook_id":"NOTEBOOK_ID","prompt":"Resuma as fontes em uma frase."}' -ContentType "application/json"Com NOTEBOOKLM_REAL_WRITE_ENABLED=false, o create/add-text real deve voltar bloqueado. Para writes reais, habilite a flag somente depois dos gates do DANTE estarem validados.
Worker secrets
Configure no Cloudflare:
npx wrangler secret put NOTEBOOKLM_RUNNER_URL
npx wrangler secret put NOTEBOOKLM_RUNNER_TOKEN
npx wrangler secret put NOTEBOOKLM_ENABLED
npx wrangler secret put NOTEBOOKLM_REAL_WRITE_ENABLED
npx wrangler secret put NOTEBOOKLM_REAL_ARTIFACT_GENERATION_ENABLEDMantenha NOTEBOOKLM_REAL_WRITE_ENABLED=false e NOTEBOOKLM_REAL_ARTIFACT_GENERATION_ENABLED=false ate health, auth, Admin Lock, Approval Center e Risk Manager estarem validados.
Nunca fazer
- Nao commitar
storage_state.json,NOTEBOOKLM_AUTH_JSON, cookies, tokens ou emails sensiveis. - Nao aceitar requests diretos do browser no runner.
- Nao habilitar write/artifacts reais sem gates.
- Nao retornar tracebacks com secrets.
- Nao marcar DANTE como
real_controlledantes de/health,/auth/statuse smoke tests passarem.
