Skip to content

v0.10.0 — bring your own Google Cloud project (bundled OAuth retired)

Choose a tag to compare

@github-actions github-actions released this 07 Jul 14:13

What's New

This is a breaking release that resolves the OAuth verification saga for good, lays the foundation that powers AdLoop Cloud, and fixes three reported bugs.

⚠️ BREAKING — bundled OAuth credentials removed

AdLoop no longer ships a built-in OAuth client. The backstory: Google's verification review for the shared client never completed after 2+ months, and we learned that the 100-user limit on unverified apps is a lifetime cap per project — revoked users don't free up seats, and there is no way to reset the counter. Waiting was not a plan, so we removed the dependency entirely.

Every install now uses its own (free) Google Cloud project:

  • New users: run adloop init — the wizard walks you through the ~5-minute one-time setup (create project, enable three APIs, create an OAuth client). No shared caps, no dependency on anyone's verification review, your own consent screen.
  • Existing users on bundled credentials (≤ 0.9): your stored token can no longer be refreshed after upgrading. Run adloop init once to switch to your own project — your config, rules, and everything else carry over.
  • Prefer zero setup? AdLoop Cloud is the hosted version: connect Google in two clicks, no Cloud project, no developer token, EU-hosted. Free beta, limited seats.

Discussion #13 is closed accordingly — the "pending verification" state it tracked no longer exists.

Multi-tenant runtime foundation

The internals that let one AdLoop process serve many isolated tenants (this is what runs AdLoop Cloud):

  • New adloop.runtime module: request-scoped config + tenant context (contextvars), explicit deployment modes (stdio/server), and pluggable backends for credentials, plan storage, and audit logging.
  • import adloop.server is now side-effect-free — no config loading or file writes at import time.
  • Plan store keys plans per tenant; the audit sink is swappable (file-based remains the OSS default — behavior for normal stdio users is unchanged).

Security — SSRF guard on ad/sitelink URL validation

Reported in #41 (thanks for the detailed PoC): final_url validation used to fetch caller-controlled URLs with no restrictions. Now:

  • http/https scheme allowlist (no more file:// existence oracle)
  • Private, loopback, link-local, and cloud-metadata addresses are rejected after DNS resolution
  • Every redirect hop is re-checked through the same guard
  • draft_image_assets refuses local file reads when running in server mode

Bug fixes

  • attach_shared_set_to_campaigns / detach_... crashed at apply time (#40) — partial_failure is now set on the request object instead of being passed as an unsupported kwarg. Fix by @illia-sapryga (cherry-picked from PR #34 with attribution — thanks!).
  • run_gaql hid Google's real error message (#47) — errors now surface the structured failure.errors[] detail (exact code + Google's own message naming the field at fault), with the generic hint only appended as a suffix.
  • run_gaql dropped nested message values (#47) — nested proto messages (targeting settings and the like) now serialize to plain dicts instead of collapsing to their str() repr. Text-carrying assets (RSA headlines etc.) still return plain strings.

Docs

  • The orchestration rules gained an audience-type ↔ campaign-type compatibility matrix (#44) — including the explicit callouts that custom segments cannot attach to Search campaigns and that Performance Max only takes audience signals, never hard targeting.

Tests: 293 pass (was 262 in 0.9.0 — new runtime, GAQL-error, and shared-set suites).

Full Changelog: v0.9.0...v0.10.0