Re-read localStorage after network calls in flush() to avoid
discarding entries added by concurrent enqueue() calls. Also
call flush() in the cookie fast-path of load() so queued
consent retries are always drained.
Signed-off-by: Émile Ré <emile@getprobo.com>
- Rename MAX_RETRIES to MAX_ATTEMPTS (3 total) to fix
misleading naming
- Skip retry loop on caller-initiated aborts so explicit
cancellations terminate immediately
- Preserve original script type via data-type attribute
instead of always forcing text/javascript
- Recreate MutationObserver when consent changes so newly
added elements use fresh consent data
- Fix package.json exports: point main at ESM bundle and
add proper exports map with IIFE as separate entry
Signed-off-by: Émile Ré <emile@getprobo.com>
Reduce timeout/retry defaults (5s timeout, 2 retries, 500ms base delay)
to cap worst-case page-load blocking at ~12s instead of ~36s.
Add a localStorage-backed queue that persists failed consent POSTs and
replays them on next page load, closing the compliance gap where a
network failure could permanently lose the server-side audit record.
Signed-off-by: Émile Ré <emile@getprobo.com>
Unblock scripts, iframes, images, and other resource-loading
elements marked with data-cookie-consent when their category
is consented. Uses MutationObserver for dynamically added
elements. Restricted to known resource tags with data-href
support for link elements.
Signed-off-by: Émile Ré <emile@getprobo.com>
CookieBannerClient wraps the cookie banner REST API with
methods to load config, accept/reject/customize consent,
and manage visitor identity. Consent state is persisted in
a probo_consent cookie to skip API calls on return visits,
with version-aware invalidation and configurable expiry.
Signed-off-by: Émile Ré <emile@getprobo.com>
Introduces fetchJSON<T> with timeout, exponential backoff
with jitter on network errors and 5xx/429, and error classes
that match the cookie banner API error shape.
Signed-off-by: Émile Ré <emile@getprobo.com>
Introduce the boilerplate for a new public npm package that will
serve as the cookie consent banner JavaScript SDK. The package
uses esbuild to produce both an IIFE bundle (for script tag
embedding) and an ESM module. Versioning is independent from the
monorepo: CI compares package.json against npm and only publishes
when the version changes.
Signed-off-by: Émile Ré <emile@getprobo.com>
Statements of Applicability are no longer exported as one-off PDFs.
Instead, each SOA owns a persistent document that accumulates versions
over time, following the same publish/approve lifecycle as authored
documents.
Publishing without approvers publishes immediately; publishing with
approvers creates a draft pending approval via the existing quorum
system. SOAs can also store default approvers that are pre-populated in
the publish dialog.
The SOA is removed from the snapshot system — applicability statements
are now queried directly (snapshot_id IS NULL) rather than through
snapshot copies.
A standalone migration script (cmd/migrate-soa-snapshots-to-documents)
converts existing SOA snapshots into documents with proper ProseMirror
content, preserving version history and approval decisions.
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
Replace the draft-only unique index with one covering both DRAFT and
PENDING_APPROVAL statuses, ensuring a document can have at most one
active version at a time. Deduplicate any existing data before creating
the index, prioritizing PENDING_APPROVAL over DRAFT.
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
Merge type-only schema files (country_code.graphql, pagination.graphql,
identity.graphql) back into base.graphql for both trust/v1 and console/v1.
These standalone files had no corresponding _resolvers.go files, causing
gqlgen v0.17.87's Rewriter.getSource() to panic with 'slice bounds out
of range' when running go generate. By consolidating them into
base.graphql (which already has base_resolvers.go), gqlgen can process
the schema without needing separate resolver files for pure-type
definitions.
Add server-side validation in BulkRequestSignatures, RequestSignature,
and RequestApproval to load the referenced profiles and verify none
have an ended contract before proceeding. Returns ErrProfileContractEnded
if a profile's contract_end_date is in the past, surfaced as a CONFLICT
GraphQL error in all three resolvers.
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
Move the paginated people query from PeopleGraph.ts into
SignatureDocumentsDialog.tsx following colocated query conventions
and pass excludeContractEnded: true to hide people whose contract has ended.
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
Align MCP and n8n document listing endpoints with the GraphQL console
API so every document filter is available across all three interfaces.
MCP:
- listDocuments: add status filter, default to ACTIVE
- listDocumentVersions: add statuses filter
- DocumentOrderField: add UPDATED_AT
n8n:
- document getAll: add query, documentTypes, classifications, status filters (default ACTIVE)
- document getAllVersions: add statuses filter
- document getAllSignatures: add states and activeContract filters
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
- Remove the right-side drawer and display document properties in a
3-column Card below the page header
- Move status badge to the PageHeader (right-aligned, matching
compliance page style)
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
Strip forwarded headers (Forwarded, X-Forwarded-For, X-Real-Ip)
from requests originating from untrusted proxies at the HTTP
server level, reusing the existing proxy-protocol trusted-proxies
config. The clientip package is now a pure extraction helper;
context plumbing and middleware wrappers are removed.
Signed-off-by: Émile Ré <emile@getprobo.com>
Add coredata guide section on using Go enum constants as named
SQL parameters instead of hardcoded string literals. Fix mixed
inline/multiline RenderJSON call in cookie banner handler.
Signed-off-by: Émile Ré <emile@getprobo.com>
Move clientIP extraction into a reusable pkg/server/api/clientip
package with RFC 7239 Forwarded header support. Add
pkg/server/jsonutil with helpers for common HTTP error responses
(RenderForbidden, RenderInternalServerError, RenderNotFound,
RenderBadRequest) and use them in the cookie banner handlers.
Signed-off-by: Émile Ré <emile@getprobo.com>
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>
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>
Introduce /cookie-banner/v1/{bannerID}/config endpoint for the JS SDK.
The custom CORS middleware validates each request origin against the
specific banner being requested, preventing cross-customer leakage.
Signed-off-by: Émile Ré <emile@getprobo.com>
Strip www. prefix and trailing slash from origin when creating or
updating a cookie banner so CORS lookups match regardless of whether
the customer's site redirects www to the apex domain.
Signed-off-by: Émile Ré <emile@getprobo.com>
Move OIDCProviderInfo type into base.graphql alongside its query field
in both connect and trust APIs, removing orphan oidc.graphql files.
Split connect profile.graphql into three domain files: profile (with
user mutations), membership, and invitation.
Signed-off-by: Émile Ré <emile@getprobo.com>
The make relay target merges split graphql files into a single
schema.graphql before running the Relay compiler. All CI workflows,
the generate target, and documentation now use make relay.
Signed-off-by: Émile Ré <emile@getprobo.com>
Relay v20 treats extend type in server schema as client extensions,
causing all mutation fields to have "text": null in generated artifacts.
Replace the cat-based schema concatenation with a merge script that
collects all extend type Mutation blocks and produces a single
type Mutation definition. The split graphql/ files remain the source
of truth for gqlgen; the merged schema.graphql is generated for Relay.
Signed-off-by: Émile Ré <emile@getprobo.com>
Update GNUmakefile targets to use wildcard graphql/*.graphql
prerequisites instead of the deleted schema.graphql files, and
remove v1_resolver.go from build prerequisites.
Also update .gitattributes, package.json graphql.schema path,
and AGENTS.md.
Signed-off-by: Émile Ré <emile@getprobo.com>
Change gqlgen filename_template from {name}.resolvers.go to
{name}_resolvers.go across all three APIs for consistent Go naming.
Signed-off-by: Émile Ré <emile@getprobo.com>
Move Identity, Organization, Viewer, PageInfo, OrderDirection,
CountryCode, OIDCProviderInfo, File, and ReauthenticationReason out
of base.graphql into their own dedicated files across all three APIs.
base.graphql now only contains directives, scalars, Node interface,
Query type, and an empty Mutation type (required by Relay
schemaExtensions). Entity files use extend type Mutation for their
mutations.
Signed-off-by: Émile Ré <emile@getprobo.com>