Commit Graph

4481 Commits

Author SHA1 Message Date
Aurélien Sibiril
cd8ddd8db5 Add public-client (CIMD) OAuth support
Public clients authenticate with PKCE and no client secret, using a
hosted Client ID Metadata Document (CIMD) as the client_id.

Add a no-secret token-endpoint mode, derive the state-token salt and the
PKCE verifier from a server-side key so the verifier never appears in
the signed-but-unencrypted state, and expose Registration.PublicClient,
Registry.PublicClients and the CIMD metadata path for provider wiring.

Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
2026-06-03 17:00:06 +02:00
Émile Ré
8f40f460d4 Release probod/v0.202.2
Signed-off-by: Émile Ré <emile@probo.com>
2026-06-03 16:42:35 +02:00
Émile Ré
2221dc688a Upgrade go to 1.26.4
Signed-off-by: Émile Ré <emile@probo.com>
2026-06-03 16:39:43 +02:00
Émile Ré
4553407591 Release probod/v0.202.1
Signed-off-by: Émile Ré <emile@getprobo.com>
2026-06-03 16:22:32 +02:00
Émile Ré
e29dc4fee9 Update trivy sarif upload action to associate to branch ref
Signed-off-by: Émile Ré <emile@probo.com>
2026-06-03 16:20:12 +02:00
Ludovic Vielle
ab05e501ac Release @probo/cookie-banner/v0.8.0 2026-06-03 14:48:48 +02:00
Ludovic Vielle
460decaa1b Release probod/v0.202.0
Signed-off-by: Ludovic Vielle <ludovic@probo.com>
2026-06-03 14:47:34 +02:00
Ludovic Vielle
5ec4656cfe Fix contrib/seed.sh script
`jo -a` is waiting on stdin; adding `< /dev/null` allows to
generate an empty array without waiting on stdin.D

Signed-off-by: Ludovic Vielle <ludovic@probo.com>
2026-06-03 11:04:16 +02:00
Ludovic Vielle
9ea9769873 Fix lint issues from previous commit
Signed-off-by: Ludovic Vielle <ludovic@probo.com>
2026-06-03 10:51:35 +02:00
Ludovic Vielle
6c072a2f7b Use stable files API URLs for vendor logo fields
CommonThirdParty.logoUrl and TrustCenterReference.logoUrl were
returning expiring S3 presigned URLs, which break if cached or
shared past their TTL.

Replace with stable /api/files/v1/{id} application URLs.
file.Service now generates these via baseurl; a new filesign
package owns presigning for the files/v1 HTTP handler that
does the internal redirect.

Signed-off-by: Ludovic Vielle <ludovic@probo.com>
2026-06-03 10:30:29 +02:00
Bryan Frimin
383ea5a2d4 Retrict some query and mutation to only session
Signed-off-by: Bryan Frimin <bryan@probo.com>
2026-06-02 17:43:21 -07:00
Émile Ré
9c4b6aff18 Link cookie policy doc and revamp tracker rows
Expose the generated cookie policy Document on the CookieBanner
GraphQL type through a nullable policyDocument field and resolver,
and surface a link to it from the banner configuration header next
to the origin and ID. The link is hidden until a banner version is
published and the document exists.

Rework the tracker table rows: drop the Source column in favour of
a tracker Type badge, and move each tracker's description inline
beneath its name (and into the add/edit row inputs) instead of a
separate Description column.

Signed-off-by: Émile Ré <emile@probo.com>
2026-06-02 16:21:01 +02:00
Émile Ré
e9b36bed14 Number tracker policy section titles
Other generated policy documents number their top-level sections.
Apply the same convention to the cookie and tracking technologies
policy by prefixing each "##" heading with a running counter. A
counter is used rather than literal numbers so the sequence stays
contiguous when the optional "Third parties that set trackers"
section is omitted.

Signed-off-by: Émile Ré <emile@probo.com>
2026-06-02 16:21:01 +02:00
Émile Ré
300e1b6688 Show tracker type in cookie banner trackers list
The themed banner and headless cookie list exposed only the tracker
name, description, and duration, so trackers that share a name but
differ in technology were indistinguishable to visitors.

Add the tracker type to the headless CookieItem model and render it as a
labeled "Type: <value>" detail under the tracker name, with an explicit
aria-label for assistive technology. Tracker type names are Web platform
API names (Cookie, Local storage, IndexedDB, ...), so they are kept
canonical while only the surrounding label is localized.

Signed-off-by: Émile Ré <emile@probo.com>
2026-06-02 16:21:01 +02:00
Émile Ré
f702edabf0 Move cookie banner display tab first
Signed-off-by: Émile Ré <emile@probo.com>
2026-06-02 16:21:01 +02:00
Émile Ré
6eff9ecb97 Move tracker policy rendering out of write transaction
PublishTrackerPolicy ran template execution, markdown parsing and JSON
marshaling inside the WithTx callback, holding a write transaction open
across CPU-bound work. Split it into a read phase (WithConn) that
gathers data and renders the document, followed by a write phase that
persists the document and version, mirroring PublishThirdPartyList.

Also sort the generated third-party rows before returning. LoadByIDs
has no ORDER BY, so the policy document could otherwise be emitted in a
different row order on each regeneration.

Signed-off-by: Émile Ré <emile@probo.com>
2026-06-02 16:21:00 +02:00
Émile Ré
73854f98cb Show tracker type in cookie tracking policy
Trackers sharing a display name can differ in type, so the generated
cookie and tracking technologies policy was ambiguous without it. Carry
the tracker type through the banner version snapshot and surface it as a
dedicated column in the policy table.

Stop the snapshot from dropping non-cookie trackers so storage, IndexedDB
and cache technologies appear in the policy and served banner config with
their real type. Duration now reflects the type when no max-age applies:
session storage clears with the tab, the remaining storage technologies
persist. Legacy snapshots predate the field and only ever held cookies,
so GetSnapshot backfills an empty type as COOKIE, keeping the non-null
GraphQL enum and policy output valid without a migration.

Signed-off-by: Émile Ré <emile@probo.com>
2026-06-02 16:21:00 +02:00
Émile Ré
346ca94d38 Include website origin in tracker policy title
An organization can run several websites, each with its own banner and
generated policy. Add the banner origin to the document title and the
rendered heading so the policies are distinguishable in document lists
and the trust center.

Signed-off-by: Émile Ré <emile@probo.com>
2026-06-02 16:21:00 +02:00
Émile Ré
4d64b1f937 Add worker that regenerates the tracker policy
Add a poll-based tracker policy worker that claims banners flagged for
policy generation, clears the flag, and rebuilds the cookie and
tracking technologies policy from the latest published snapshot. A
banner that lost its published version is skipped rather than failed.

Wire the worker into probod with crash propagation and graceful
shutdown alongside the other tracker workers.

Signed-off-by: Émile Ré <emile@probo.com>
2026-06-02 16:21:00 +02:00
Émile Ré
d68df53a6d Generate tracker policy document from banner snapshot
Add a markdown "Cookie and Tracking Technologies Policy" template in
the policy-writer tone that covers cookies and other tracking
technologies, with GDPR, UK GDPR, FADP, and CCPA/CPRA privacy-rights
framing. Convert it to ProseMirror JSON and add PublishTrackerPolicy,
which builds the document from a banner's latest published version
snapshot and its tracker third parties. The document is GENERATED and
private in the trust center, linked to the banner via
policy_document_id.

Add CookieItem.HumanizedDuration so server-rendered lifetimes match the
banner's humanizeDuration helper, and reword the publish trigger and
backfill comment to reflect the broader tracker scope.

Signed-off-by: Émile Ré <emile@probo.com>
2026-06-02 16:20:59 +02:00
Émile Ré
f6eeeb675f Track cookie policy generation requests on publish
Add policy_document_id and policy_generation_requested_at columns to
cookie_banners and backfill banners that already have a published
version, so existing live banners get a policy on the worker's first
pass.

Flag the banner for policy generation inside the publish transaction so
generation is requested only when a cookie banner version is published,
not on draft edits. Fold policy_document_id into the existing Update so
the upcoming worker can persist the generated document id with the
scope it already holds.

Signed-off-by: Émile Ré <emile@probo.com>
2026-06-02 16:20:08 +02:00
Émile Ré
534079ddcc Release helm/v0.3.0
Signed-off-by: Émile Ré <emile@getprobo.com>
2026-06-02 12:48:12 +02:00
Émile Ré
8fee43d4dd Release @probo/n8n-nodes-probo/v0.191.0
Signed-off-by: Émile Ré <emile@getprobo.com>
2026-06-02 12:47:41 +02:00
Émile Ré
d9cf24d8fc Release probod/v0.201.0
Signed-off-by: Émile Ré <emile@getprobo.com>
2026-06-02 12:46:35 +02:00
Émile Ré
e560d04e12 Release prb/v0.191.0
Signed-off-by: Émile Ré <emile@getprobo.com>
2026-06-02 12:45:35 +02:00
Émile Ré
e55569848e Fix lint issues
Signed-off-by: Émile Ré <emile@probo.com>
2026-06-02 11:59:21 +02:00
Aurélien Sibiril
e9c4e39ed9 Log instance name resolution failures as warning
Provider 4xx on an instance-name lookup is rarely actionable -- it
usually reflects a configuration state (deleted org, revoked token)
rather than a system error worth paging on. Downgrade Probo's own
log entry to Warn. The kit/worker entry for the same failure is
still Error pending the retry-limit work, which will mark sources
synced after N attempts and stop the duplicate log line.

Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
2026-06-02 11:57:32 +02:00
Aurélien Sibiril
79bc5aa79e Guard github name resolver against empty organization
When a GitHub access-source connector has no organization configured
yet (user finished OAuth but abandoned the picker), the source-name
worker called https://api.github.com/orgs/ and got a 404 every 10
seconds, flooding logs. All other picker resolvers (sentry, gitlab,
bitbucket, heroku, asana, netlify, clickup, vercel) short-circuit to
("", nil) for empty settings -- this aligns github with them so the
worker falls into its existing "empty instance name -> mark synced
with generic name" branch instead of retrying forever.

Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
2026-06-02 11:57:30 +02:00
Sacha Al Himdani
6e7c96732f Add async third-party vetting
Queue vetting on third_parties with PENDING, PROCESSING,
COMPLETED, and FAILED states. Expose enqueue and status through
GraphQL, MCP, CLI, and n8n, validate vet requests, tune the
worker via config, and poll the detail page while vetting runs.

Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
2026-06-02 11:39:51 +02:00
dependabot[bot]
1a71d15bc5 Bump vitest from 3.2.4 to 4.1.8
Bumps [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest) from 3.2.4 to 4.1.8.
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Changelog](https://github.com/vitest-dev/vitest/blob/main/docs/releases.md)
- [Commits](https://github.com/vitest-dev/vitest/commits/v4.1.8/packages/vitest)

---
updated-dependencies:
- dependency-name: vitest
  dependency-version: 4.1.8
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
2026-06-02 09:17:46 +02:00
dependabot[bot]
a7932d9662 Bump @vitest/browser from 4.0.18 to 4.1.6
Bumps [@vitest/browser](https://github.com/vitest-dev/vitest/tree/HEAD/packages/browser) from 4.0.18 to 4.1.6.
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Changelog](https://github.com/vitest-dev/vitest/blob/main/docs/releases.md)
- [Commits](https://github.com/vitest-dev/vitest/commits/v4.1.6/packages/browser)

---
updated-dependencies:
- dependency-name: "@vitest/browser"
  dependency-version: 4.1.6
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-01 17:03:44 +02:00
Émile Ré
a617131b70 Release helm/v0.2.1
Signed-off-by: Émile Ré <emile@getprobo.com>
2026-06-01 16:48:06 +02:00
Émile Ré
66708ec053 Release probod/v0.200.1
Signed-off-by: Émile Ré <emile@getprobo.com>
2026-06-01 16:47:49 +02:00
Émile Ré
b9eac20c9c Raise tracker agent max turns to avoid errors
The tracker-mapping and common-pattern enrichment agents ran with
4 and 3 max turns. Those budgets are smaller than the tool-call
rounds their prompts authorize (two/one DB searches plus up to
three web searches) plus the forced structured-output synthesis
turn, so the run loop tripped MaxTurnsExceededError before it
could emit JSON.

Raise the budgets to 10 across every layer that drives them: the
bootstrap env-var defaults, the Helm chart values and production
example, and the in-code fallback. Update the bootstrap test
defaults to match.

Revert the prompt instructions that told the model to reserve a
turn for its final answer; the model cannot reliably count its
remaining turns, so the fix belongs in the turn budget rather
than the prompt text.

Signed-off-by: Émile Ré <emile@probo.com>
2026-06-01 16:40:03 +02:00
Émile Ré
a3b330ca29 Update tracker agents prompts
Signed-off-by: Émile Ré <emile@probo.com>
2026-06-01 16:28:24 +02:00
Émile Ré
948b3d1d85 Release helm/v0.2.0
Signed-off-by: Émile Ré <emile@getprobo.com>
2026-06-01 14:58:22 +02:00
Émile Ré
49dabedff4 Release probod/v0.200.0
Signed-off-by: Émile Ré <emile@getprobo.com>
2026-06-01 14:58:04 +02:00
Émile Ré
cdf055591b Drop obsolete LLM request sanitization test
The client no longer strips sampling parameters per model, so the
sanitization test and its capturing provider helper assert behavior
that was intentionally removed. Delete them to restore a green build.

Signed-off-by: Émile Ré <emile@probo.com>
2026-06-01 14:49:13 +02:00
Émile Ré
fb60ee1776 Copy default LLM pointers when resolving agents
ResolveAgent aliased the default config's Temperature and MaxTokens
pointers into every agent that left them unset, so all resolved
agents shared one backing value. A mutation through any of those
pointers would corrupt the default and every other agent. It also
dereferenced the default unconditionally even though it can be nil.

Allocate a fresh pointer holding a copy of the default value, and
guard against a nil default so each resolved agent owns independent
state.

Signed-off-by: Émile Ré <emile@probo.com>
2026-06-01 14:15:29 +02:00
Émile Ré
8cf770613a Drop request sanitization from LLM client
Remove sanitizeRequest and its call sites so the client no longer
strips sampling parameters based on the model registry. Unsupported
knobs are now avoided by setting the config carefully per model
rather than silently mutating outgoing requests.

Signed-off-by: Émile Ré <emile@probo.com>
2026-06-01 13:39:42 +02:00
Émile Ré
387c92d045 Fix lint
Signed-off-by: Émile Ré <emile@probo.com>
2026-06-01 13:17:18 +02:00
Émile Ré
952c427d2a Add stale recovery to tracker mapping worker
The tracker-mapping worker clears mapping_requested_at at claim time, so
a crash or hard failure between Process phases left the pattern dequeued,
unmapped, and with nothing to re-trigger it. Only an incidental sibling
remap could rescue it, so a lone pattern could stay stranded forever.

Implement the worker.StaleRecoverer interface, mirroring the enrichment
worker. ResetStaleMappings re-arms rows that were claimed but never
assigned a catalog row (common_tracker_pattern_id IS NULL) once idle past
a configurable window; a successful Process always assigns one via the
unmatched fallback, so the predicate cleanly detects interrupted runs and
self-heals after a single pass. ClearMappingRequestedAt now bumps
updated_at so the stale clock starts at claim time and the sweep never
recycles an in-flight claim.

Plumb a StaleAfter knob (default 600s) through the config struct, builder
env var, probod wiring, and Helm templates.

Signed-off-by: Émile Ré <emile@probo.com>
2026-06-01 13:17:18 +02:00
Émile Ré
587a4f63cd Stop tracker agents from inventing vendors
The identification agent attributed probo_distinct_id to Mixpanel
purely on the shared distinct_id token, and the enrichment agent
returned no description for the glob ph_phc_*_posthog because it
searched the literal "*" string and found nothing.

Tighten the identification prompt so attribution requires a perfect
pattern match or a meaningful prefix that belongs to the vendor; a
generic token behind a different prefix is not a match. Teach the
enrichment prompt to strip wildcard and variable parts before
searching, and to treat a vendor name embedded in the key as
corroboration so clearly-named trackers still get a description.

Signed-off-by: Émile Ré <emile@probo.com>
2026-06-01 13:17:18 +02:00
Émile Ré
323fe4b5c3 Skip shared infrastructure in domain matching
The deterministic tracker-mapping heuristics group patterns by shared
initiator domain, but tag managers, customer-data platforms, and
generic CDNs (Google Tag Manager, Segment, cloudfront.net, ...)
initiate trackers for many unrelated vendors. Grouping on such a
domain mis-attributes one vendor's tracker to another.

Add uri.FilterSharedInfrastructureDomains backed by a curated eTLD+1
denylist and apply it once in resolveDeterministic, so sibling
grouping, catalog domain matching, and the sibling re-enqueue cascade
all ignore shared-infrastructure hosts. Vendor-specific domains such
as google-analytics.com are intentionally kept as a same-vendor
signal. The agent path is unchanged: it still sees observed domains,
now with a prompt caveat about shared infrastructure.

Update the two sibling tests that used googletagmanager.com as the
initiator domain to a vendor domain, since that host is now stripped
before grouping.

Signed-off-by: Émile Ré <emile@probo.com>
2026-06-01 13:17:17 +02:00
Émile Ré
54c05ebe6a Harden catalog vendor resolution and agent prompt
Address review feedback on the agent-driven tracker catalog path:

- Return initiator-domain load failures instead of swallowing them,
  so the worker retries rather than running the agent on partial
  context.
- In the resolver, treat only ErrResourceNotFound as a catalog miss
  and propagate genuine name/slug lookup errors.
- Insert the new vendor inside a savepoint and, on the slug
  unique-violation race, reload and return the winning row instead of
  aborting the caller's transaction.
- Stop seeding common_third_party_domains from observed initiator
  domains. They are a co-occurrence signal, not verified ownership,
  and writing them into the global cross-tenant catalog pollutes the
  domain-based matcher. The curated seed owns that data.
- Warn the mapping agent that observed domains may belong to shared
  CDNs, tag managers, or hosting infrastructure rather than the
  vendor, so it does not attribute on that basis alone.
- Extract a shared tracker-identification prompt helper and move the
  common-pattern identification prompt next to the enrichment agent.

Signed-off-by: Émile Ré <emile@probo.com>
2026-06-01 13:17:17 +02:00
Émile Ré
c8b7615046 Fix lint issues
Signed-off-by: Émile Ré <emile@probo.com>
2026-06-01 13:17:17 +02:00
Émile Ré
8d5571cf91 Move common third-party resolver to thirdparty pkg
resolveOrCreateCommonThirdParty lived as a package-level helper in the
tracker mapping worker, but the common pattern enrichment worker now
reuses it. Homing shared catalog logic in a mapping-named file made the
enrichment worker quietly depend on the mapping worker's file, and it is
not a mapping concern.

Move it to pkg/thirdparty as exported ResolveOrCreateCommonThirdParty,
decoupled from cookiebanner's TrackerMappingAgentResult (it now takes a
name and category) to avoid an import cycle. It stays a transaction-
scoped free function so both workers compose it into their own tx for
atomicity rather than receiving a service that owns its own connection.

Relocate the catalog dedup DB test alongside it.

Signed-off-by: Émile Ré <emile@probo.com>
2026-06-01 13:17:17 +02:00
Émile Ré
9b03d199da Reuse mapping agent to attribute trackers in enricher
The enrichment worker no longer invents a description when a tracker's
purpose cannot be substantiated; it records an empty description and
marks the row enriched so the stale-recovery loop does not retry it.

Vendor identification is the mapping pipeline's job, so the enricher
reuses the existing tracker-mapping agent to attribute a third party
for an unlinked common pattern before describing it. A confident
catalog match seeds the enrichment prompt and links the pattern, but
the enricher never creates or overrides an attribution.

When a blank, unlinked catalog row later gains a third party through
the mapping pipeline's upsert, enrichment is re-armed so the now-known
vendor gets a second, better-informed description attempt.

Signed-off-by: Émile Ré <emile@probo.com>
2026-06-01 13:17:17 +02:00
Émile Ré
7360c6eb27 Raise default agent token budget for reasoning models
The tracker mapping, common-pattern enrichment, and third-party
disambiguation agents default to a small max-tokens budget on the
premise that their final output is tiny structured JSON. On
reasoning models such as the GPT-5 family, reasoning tokens count
against max_tokens, so a small budget is consumed by reasoning and
the JSON is truncated, surfacing as "unexpected end of JSON input".

Raise the defaults to 4096 (1024 -> 4096 for tracker mapping, 512
-> 4096 for disambiguation) to leave headroom for the reasoning
phase. Update the bootstrap builder default, its test, and the
production values example to match.

Signed-off-by: Émile Ré <emile@probo.com>
2026-06-01 13:17:17 +02:00
Émile Ré
b6d0b64224 Skip mapping when tracker pattern deleted concurrently
The tracker-mapping worker runs its LLM and web-search phases
between short transactions and holds no row lock across them. The
pattern-analysis worker can merge a pattern into a glob and delete
it in that window, so the final UpdateMapping then fails with
ErrResourceNotFound and the task errors out spuriously.

A vanished pattern has nothing left to map, so treat the concurrent
delete as a no-op: log it and return nil instead of failing.

Signed-off-by: Émile Ré <emile@probo.com>
2026-06-01 13:17:17 +02:00