19 Commits

Author SHA1 Message Date
Sacha Al Himdani
4c57d201a4 Make license declarations consistently MIT
The source headers, LICENSE files, and license metadata had drifted
apart. Align the entire project to MIT:

- Convert every source-file header to the MIT text across all comment
  styles (Go, TS, TSX, JS, MJS, SQL, CSS, GraphQL, shell), including
  SPDX-License-Identifier tags
- Set the root and cookie-banner LICENSE files to the MIT text with a
  "MIT License" title line
- Switch the package.json license fields, Docker image label, and
  cookie-banner README to MIT
- Update docs and the genmodels header generator accordingly
- Normalize copyright lines to a single format
  (Copyright (c) <year(s)> Probo Inc <hello@probo.com>.): unify the
  hello@getprobo.com and hello@probo.inc emails to hello@probo.com and
  the comma-separated years to a hyphenated range

Genuine third-party references are intentionally left untouched: the
Lucide icon attributions (Lucide is ISC) and the trivy dependency
license allowlist.

Signed-off-by: Sacha Al Himdani <sacha@probo.com>
2026-07-13 16:21:14 +02:00
Émile Ré
ed9831a734 Default cookie consent to GDPR and track its source
When IP geolocation does not resolve a country, or resolves one with no
known cookie-consent regulation (common on localhost and unmapped
regions), the banner previously fell back to OPT_OUT with no recorded
regulation. Apply GDPR (OPT_IN) as the safe default in that case so the
strictest consent model wins when origin is unknown.

To keep consent records auditable, stamp each one with a regulation
source of DETECTED (resolved from geolocation) or DEFAULT (fell back to
GDPR). The shared cookiebanner.ResolveRegulation helper centralizes the
decision for both the config and consent endpoints, and the new value is
exposed through GraphQL, MCP, the CLI, the n8n node, and the console
consent-records views.

Signed-off-by: Émile Ré <emile@probo.com>
2026-06-10 17:49:41 +02:00
Sacha Al Himdani
9ac71f948f Update contact email to hello@probo.com
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
2026-06-09 16:45:23 +02:00
Bryan Frimin
bd04f1812a Style
Signed-off-by: Bryan Frimin <bryan@probo.com>
2026-05-23 14:07:37 -07:00
Bryan Frimin
b39d880c0f Switch AuthorizationAttributes to batch and add AuthorizeBatch
Change AuthorizationAttributer.AuthorizationAttributes to take a slice
of resource ids and return policy.AttributesByID, so a single SQL
round-trip can load condition attributes for a whole batch. All
coredata implementations are migrated to a single
`WHERE id = ANY(@resource_ids::text[])` query that returns only the
rows it finds.

Authorizer gains:
  - AuthorizeBatch — all-or-nothing across a homogeneous (same entity
    type, same organization) resource set; rejects mixed entity types,
    mixed organizations, and empty batches with structured errors.
  - AuthorizeMulti — heterogeneous evaluation that returns one error
    per item and writes audit log entries in a single bulk insert.

The single-resource Authorize is rewired to delegate to AuthorizeBatch
so all paths share the same condition evaluation and audit logging.
recordAuditLog is split into buildAuditLogEntry plus a batch insert.

Tests cover the new batch and multi paths, mixed/empty/unsupported
resource cases, audit log batching, and dry-run behaviour.

Signed-off-by: Bryan Frimin <bryan@probo.com>
2026-05-23 13:31:36 -07:00
Émile Ré
9156d6a16a Add wsl linter and fix
Signed-off-by: Émile Ré <emile@probo.com>
2026-05-20 09:27:28 +04:00
Émile Ré
e739473bcd Derive consent mode from geolocation, not banner config
The consent mode is now determined dynamically by the visitor's
country and its applicable regulation. The configured consent_mode
column is dropped from cookie_banners and added to
cookie_consent_records to persist the geo-derived mode at
consent-recording time. When no regulation matches, the default
is OPT_OUT.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-05-13 12:49:13 +04:00
Émile Ré
5f4fd3c427 Surface regulation and user agent across consent record API layers
Signed-off-by: Émile Ré <emile@getprobo.com>
2026-05-07 15:13:48 +04:00
Émile Ré
b4d8bd8de2 Add country code to consent records
Store the visitor's resolved country code alongside regulation in
cookie_consent_records so downstream surfaces can expose it.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-05-07 15:13:48 +04:00
Émile Ré
4598506076 Extract shared types and add Regulation type with parsing methods
Move cookie banner types (CookieItem, Category, Regulation, BannerConfig,
etc.) into a dedicated types.ts file. Add a coredata.Regulation type with
parsing, JSON marshaling, and database scanning methods. Hardcode the
geoloc-import data directory since the submodule path is fixed.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-05-07 15:13:42 +04:00
Émile Ré
f695b90b19 Store detected regulation in consent records and expose it in SDK
Add a regulation column to cookie_consent_records so each consent
captures which privacy law was in effect. Thread the value from the
handler's geoloc resolution through the service into the DB insert.

On the SDK side, add a Regulation union type to BannerConfig and expose
it via a getter on CookieBannerClient and in the probo-ready event
detail so themed-banner consumers can adapt their UI per regulation.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-05-07 15:13:42 +04:00
Émile Ré
9b83e319a0 Add consent record detail page
Display record attributes and parsed consent data with
per-category consent state and cookies from the banner
version snapshot. The page lives outside the config layout
with its own breadcrumb navigation.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-04-28 15:28:17 +04:00
Émile Ré
d7eec08cd2 Add banner ID to probo_consent cookie
Include a `bid` field in the consent cookie so it explicitly
identifies which cookie banner it belongs to, making validation
direct instead of relying on the visitor ID as an implicit
discriminator. Existing cookies without `bid` self-heal on the
next load by falling through to the API fetch.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-04-24 18:48:40 +04:00
Émile Ré
8ec434d67e Fix review comments on cookie banner API
Parse only the first IP from X-Forwarded-For to prevent
the full chain from bypassing anonymization. Add secondary
sort key for deterministic consent selection. Reject origins
with empty port suffix in the validator.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-04-15 10:21:00 +04:00
Émile Ré
30a86a91f1 Add cookie banner public API endpoints
Implement config, consent retrieval, and consent recording
endpoints for the JS SDK. IP addresses are anonymized (last
octet zeroed for IPv4, /48 mask for IPv6) before storage.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-04-15 10:21:00 +04:00
Émile Ré
61cd3244df Denormalize organization_id onto cookie entities
Add organization_id to cookie_banner_versions, cookie_categories,
and cookie_consent_records so AuthorizationAttributes can return
it directly without JOINing back to cookie_banners. Document the
pattern in contrib/claude/coredata.md.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-04-13 14:52:21 +04:00
Émile Ré
88315f51e1 Add cookie banner versioning with JSONB snapshots
Introduce append-only cookie_banner_versions table with a JSONB
snapshot of consent-relevant configuration (privacy policy URL,
consent mode, expiry, categories and their cookies). Each version
has its own state (DRAFT/PUBLISHED) separate from the banner
lifecycle.

Replace the banner state enum (DRAFT/PUBLISHED/DISABLED) with a
simpler ACTIVE/INACTIVE toggle. Link consent records to the
specific published version the visitor accepted.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-04-13 14:52:20 +04:00
Émile Ré
c594b10e9f Add consent record operations and rename ConsentRecord to CookieConsentRecord
Signed-off-by: Émile Ré <emile@getprobo.com>
2026-04-13 14:50:43 +04:00
Émile Ré
e9330171fe Add new models
Signed-off-by: Émile Ré <emile@getprobo.com>
2026-04-13 14:50:41 +04:00