Use typed ErrDocumentVersionNotPublished instead of plain fmt.Errorf in
signature request methods, and add missing ErrResourceNotFound handling
across document resolvers that were returning Internal for expected errors.
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
Authenticate to Docker Hub in jobs that pull images (build-snapshot-docker,
test-e2e, build-docker) to avoid unauthenticated rate limits.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
Avoid unnecessary Docker registry checks on stack-up by setting
pull_policy: missing on every service. Also pin the tempo image to its
sha256 digest for reproducibility, consistent with all other services.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
The absolute-positioned collapse button overlapped the nav items
whenever the sidebar content exceeded the viewport. With the page now
scrolling at the document level, the overlap became permanent.
Restructure the aside as a flex column: nav list in a flex-1 region,
collapse button in a sticky bottom-0 container with a border-t
separator. The button pins to the viewport bottom while scrolling and
never overlaps the items above.
Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
The previous layout nested two scroll regions, sidebar and main, each
with its own scrollbar. Combined with the fixed 100vh flex container,
any child overflow (notably the sidebar nav) spilled past bg-level-0
and exposed the browser's default white background -- most visible in
dark mode as a white strip at the bottom of the viewport.
Switch to a single page-level scroll: pin the header with fixed, let
the wrapper grow with min-h-screen, and drop main's internal overflow.
The drawer becomes fixed too so it stays pinned while the page scrolls
and gets an opaque background to cover scrolled content behind it.
Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
The flush function pruned expired entries in memory but re-read from
localStorage before writing back, so the age filter was never persisted.
Apply the age cutoff to the final write filter and remove the early
return that skipped the write entirely on full send failure.
Signed-off-by: Émile Ré <emile@getprobo.com>
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>