Rate limits no longer inflate ssl_retry_count into an instant FAILED
path. Centralize outcomes in decideProvisioningOutcome, keep ACME
order state on transient and rate-limit errors, bound each Process
tick with a timeout, and block on FOR UPDATE when persisting a
freshly issued certificate.
Signed-off-by: Ludovic Vielle <ludovic@probo.com>
Signed-off-by: Bryan Frimin <bryan@probo.com>
Store a short entity name instead of the full home
heading so orgs can brand portals for sub-entities.
Restore hero i18n composition and keep the English
document title composed from the entity name.
Signed-off-by: Émile Ré <emile@probo.com>
The UI composed "Compliance at {{name}}." via i18n while the
DB only held the org name. Persist the full heading, backfill
existing rows, and drop the unused heroTitle locale keys.
Also use clientip.Extract for NDA signing events and default
the portal base domain to probopage.localhost for local runs.
Signed-off-by: Bryan Frimin <bryan@probo.com>
Stop aliasing compliancePortalVisibility to compliancePageVisibility in
document, file, and audit list items so the console matches the renamed
GraphQL field.
Signed-off-by: Bryan Frimin <bryan@probo.com>
Point compliance-page screens and Relay graphs
at the renamed Compliance Portal API fields and
mutations.
Signed-off-by: Bryan Frimin <bryan@probo.com>
Authorization-code exchange used ExternalClientID, which
is empty for registered clients and broke e2e token
grants. Pass the authenticated client's GID instead.
Also satisfy wsl_v5 blank-line rules, wire the delete
reference dialog through its mutation hook, and name the
filemanager logger.
Signed-off-by: Bryan Frimin <bryan@probo.com>
Align console references and OAuth branding with the
compliance-page model, and fix certificate cache eviction,
portal OAuth handlers, and magic-link edge cases left after
the trust-center rename.
Signed-off-by: Bryan Frimin <bryan@probo.com>
Generate MCP types before complianceportal, stub apps/trust
dist for embed, fix domains section indent, and start the
compose stack only after bin/probod is built.
Signed-off-by: Bryan Frimin <bryan@probo.com>
Workflows still stubbed apps/trust/dist after the rename, so
go:embed and frontend builds failed. Also make step-ca writable
on Linux CI and fix domains section indent lint.
Signed-off-by: Bryan Frimin <bryan@probo.com>
Replace flattened SSL fields with a certificate
relation loaded through certmanager, and resolve
domain slots from IDs already on the trust center
instead of reloading the compliance page.
Signed-off-by: Bryan Frimin <bryan@probo.com>
Keep GraphQL-aligned TrustCenterReferenceGraph naming, drop the
unused domain redirect route, and tighten related compliance
page UI review nits around magic-link navigation and links.
Signed-off-by: Bryan Frimin <bryan@probo.com>
Remove the separate portal login page, show OAuth client branding on
sign-in, and preserve authorize continue URLs across auth methods.
Signed-off-by: Bryan Frimin <bryan@probo.com>
Surface dedicated routes when a portal magic link is expired or
already used, and add the portal login entry point in console.
Signed-off-by: Bryan Frimin <bryan@probo.com>
Store website, email, and headquarters on the trust center so
public and admin surfaces read branding from one place. Drop the
trust API organization type and wire console, MCP, CLI, and apps
through the updated schema.
Signed-off-by: Bryan Frimin <bryan@probo.com>
Decompose the brand page into profile, domains, visual identity, and
custom link sections, and move frameworks and the NDA card onto the
overview page as dedicated fragment components. Remove the standalone
domain page and redirect its route to brand, where domains now live.
Signed-off-by: Bryan Frimin <bryan@probo.com>
Introduce the app-bound useMutation primitive and rename the trust center
graph and reference hooks to compliance page, moving the shared reference
dialogs alongside them. Sweep the compliance page subpages onto the new
mutation hook and the compliance-portal permission strings, and strip the
profile fields from the organization settings form.
Signed-off-by: Bryan Frimin <bryan@probo.com>
Emit lifecycle events for rights requests created through either the
console or compliance portal. Keep webhook subscription choices in sync
across every API and client surface.
Signed-off-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Sacha Al Himdani <SachaProbo@users.noreply.github.com>
Signed-off-by: Cursor Agent <cursoragent@cursor.com>
The previous label implied review plus approval, but the action only
approves. Update the consent text to match the new button wording.
Signed-off-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
Let trust-portal data subjects submit and track GDPR/CCPA rights
requests. The new Data Requests page lists the viewer's own requests
and a dialog submits new ones, scoped server-side to the verified
viewer email so former or inactive users can still exercise their
rights. Submission requires magic-link sign-in (reusing the existing
gate) but not the NDA gate.
Extend the shared rights_request enums with RECTIFICATION, OBJECTION
and COMPLAINT types plus a REJECTED state, and keep the console
GraphQL, @probo/helpers and the MCP specification in sync. Expose a
trust GraphQL surface (myRightsRequests query, createRightsRequest
mutation) backed by a trust service and contact-scoped coredata
loaders.
Add the missing v2 UI kit primitives the dialog needs on top of Base
UI: a SegmentedControl radio-cards group, a form Textarea, and a
Field wrapper.
Signed-off-by: Émile Ré <emile@probo.com>
Several risk views already used "Initial" while others still showed
"Inherent". Align user-facing labels across the console, shared UI
components, CLI help, n8n fields, generated documents, and MCP
descriptions. API and database field names are unchanged.
Signed-off-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
Let admins reorder commitment groups and the cards within each group from
the console Commitments tab using up/down buttons, driven by the existing
rank-aware update mutations.
Make the (parent, rank) unique constraints on the commitment tables
DEFERRABLE INITIALLY DEFERRED. Reordering shifts several rows in one
UPDATE, which transiently duplicates a rank and tripped the immediately
enforced constraint with a 23505 error. This matches the other
rank-ordered tables (references, compliance frameworks).
Signed-off-by: Émile Ré <emile@probo.com>
The compliance portal home page rendered security-commitment cards from
a hardcoded placeholder POJO. Back them with real, per-organization data
that admins configure in the console and the portal loads over the trust
center GraphQL API.
Model two entities under the trust center: a commitment group (title,
description, rank) and a commitment card (icon, eyebrow, title,
description, rank). The card icon is a curated enum mapped to a Phosphor
icon in the portal. New entities adopt the compliance_portal_ prefix as
the start of the broader rename away from trust_center_ naming.
Expose the groups and cards read-only on the public trust API and with
full CRUD on the console API, add a Commitments tab to the compliance
page, and replace the placeholder section with a Relay-driven one.
Signed-off-by: Émile Ré <emile@probo.com>
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>
The verification-code copy handler called navigator.clipboard.writeText
directly and relied on the promise rejection for the failure toast. In
an insecure context or an unsupported embedded browser navigator.clipboard
is undefined, so the call throws synchronously before .then and neither
toast fires, leaving the user without the manual-copy guidance. Guard the
access and wrap the call in try/catch, mirroring ScopeDiagram, so the
failure toast is always shown.
Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
Two honest-feedback fixes in the API-key connect dialog. The
connection-failure toast told managed providers (Crisp) to check their
API key, but Crisp shows no key field; it now points them to the
settings and verification code instead. The verification-code copy
button showed a success toast before the clipboard write resolved; it
now confirms the write and reports a failure with a manual-copy hint,
matching how other copy buttons behave.
Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
AddAccessReviewSourceDialog had grown past a thousand lines, mixing
provider search, the API-key, client-credentials and OAuth connect
flows, and their shared mutation logic in one file.
Extract the per-protocol dialogs into _components, the shared
create-source flow into a _hooks hook, and the settings mappers and
OAuth redirect helper into _lib. Each connect dialog now owns its own
Relay operation. The API-key dialog carries the Crisp managed-connector
flow: it fetches the verification code for the entered Website ID and
distinguishes loading, ready and error states, so a failed fetch shows
an actionable error with a retry instead of a permanent spinner and a
superseded response cannot overwrite the current code.
Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
Four API-key, single-tenant (Pattern 3) connectors:
- Scaleway: secret key in the X-Auth-Token header plus an Organization ID
setting; GET /iam/v1alpha1/users (owner/member, status, two-factor),
per-connection BuildProbeURL.
- Yousign: Bearer API key; GET /v3/users (admin/owner/member, is_active);
production host with a static probe.
- Railway: Bearer account token; GraphQL me{workspaces{members}} aggregated
and deduplicated across workspaces; custom probe, since Railway returns
HTTP 200 with an errors body on a rejected token.
- Crisp: plugin token as HTTP Basic (identifier:key) plus a Website ID
setting and the X-Crisp-Tier header; GET /v1/website/{id}/operators/list,
custom probe and name resolver.
Scaleway and Crisp carry a required extra setting, so the console add-source
dialog maps organizationId/websiteId onto their scalewayOrganizationId and
crispWebsiteId API-key inputs; without that mapping the value is silently
dropped and the create is rejected.
Cassette-backed driver tests plus unit tests for the cross-workspace
deduplication, the probe contracts and the role/MFA helpers.
Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
Surface the organization risk assessments list alongside the risk register
by adding a shared Risks/Risk assessments tab bar to both list pages.
Signed-off-by: Sacha Al Himdani <sacha@probo.com>
After allow or deny succeeds, replace the consent form with a full-page
redirect message naming the application and a spinner while the browser
navigates to the OAuth callback URL.
Signed-off-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
Track a pending allow/deny action with Button loading spinners so
users cannot double-submit while the approveConsent mutation runs or
while the browser waits on the OAuth callback redirect.
Signed-off-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
Replace the deny-based restriction on granting OWNER with role-scoped
allow policies so authorization fails closed: admins may create and
update memberships only when the assigned role is not OWNER, and the
absence of a target role no longer implies permission.
To keep console UI gating accurate without loosening the base grants,
the permission field gains an optional typed options argument
(PermissionOptionsInput) that forwards target_role into the dry-run
authorization. Only the two role-related console calls (create user,
update membership) pass it; the OWNER option stays hidden for admins via
the existing assignable-roles helper.
Add a non-regression test that an admin cannot promote a member to OWNER
while still being able to change members between non-owner roles.
An organization ADMIN could hard-remove members, including OWNERs, because
removeUser (connect and MCP) only checked the weaker iam:membership-profile:delete
gate. Authorize the owner-only iam:membership:delete instead, and expose the
source attribute on MembershipProfile so the owner grant's non-SCIM condition
can match.
Consolidate ownership-grant authorization into policy for both createUser and
updateMembership: each resolver passes the requested role as a target_role
attribute and ADMIN is denied granting ownership via deny-create-owner /
deny-promote-owner. target_role is distinct from resource.role, which is the
target's current role and guards editing existing owners. With no callers left,
the iam:membership-role:set-owner action (grant and OAuth2 scope) is removed.
Also pass the authorized scope through to the RemoveUser/CreateUser services,
gate the console Remove action on iam:membership:delete, and add regression
tests plus a changelog entry.
Signed-off-by: Sacha Al Himdani <sacha@probo.com>
Add FERPA and PCI DSS framework datasets (controls plus light/dark
logos), their logo components, and register both in the framework
selector so they can be imported like the existing frameworks.
Co-authored-by: Antoine Bouchardy <antoine@getprobo.com>
Signed-off-by: Sacha Al Himdani <sacha@probo.com>
The DocumentVersionSignatureFilter exposed a field named `state` that
actually filters on the signatory's profile state, which was ambiguous
next to the signature `states` field. Rename it to `profileState`
(GraphQL) / `profile_state` (MCP) across the schema, spec, resolvers,
console app, and n8n node for clarity.
Signed-off-by: Sacha Al Himdani <sacha@probo.com>
The OAuth token and consent UIs fall back to the raw scope string when
a label is missing from scopeLabels. Add read and manage labels for
v1:resource-alias so it matches other API scopes.
Signed-off-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
Signed-off-by: Sacha Al Himdani <sacha@probo.com>
Third-party compliance report PDFs were rejected above 10MB in the
upload dialog. Increase the client-side Dropzone limit and enforce the
same cap on the backend file validator.
Signed-off-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
The compliance page rendered a Slack connector as "Connected" whenever
any SLACK connector existed for the org — including one created for an
access review with no channel configured — and only offered Disconnect,
which deletes the shared connector row.
Distinguish channel-configured connections from unconfigured ones and
let users (re)connect in place to pick a channel without first
disconnecting, reusing the existing reconnect-with-union-scopes flow via
connector_id.
Signed-off-by: Sacha Al Himdani <sacha@probo.com>
Mirror the risks refactor (c78a713): colocate routes.ts, split the
detail layout query so each child route owns its Loader + Page, rename
tabs/*Tab to resource folders with *Page, move dialogs into
_components/, and extract ThirdPartyRow with its own fragment.
Remove outlet context data passing and deprecated
loaderFromQueryLoader. Delete the monolithic ThirdPartyGraph hook,
colocating each GraphQL operation with its consumer: the create
mutation in CreateThirdPartyDialog (now useMutation + useToast) and
the third-party list queries in ThirdPartiesCell and
ThirdPartiesMultiSelectField (now useQueryLoader + usePreloadedQuery).
Signed-off-by: Sacha Al Himdani <sacha@probo.com>
Add a resource-oriented set of webhook events for the document
lifecycle. Each event carries the document plus only the sub-resource
it concerns (version, signature or approval).
Events:
- document.created / updated / archived / unarchived / deleted
- document.version.created / updated / published / rejected / deleted
- document.version.signature.requested / signed / cancelled
- document.version.approval.requested / approved / rejected / voided
Wires the new types through the migration, Go enum, GraphQL schema,
CLI, n8n nodes and the console webhooks settings UI.
Signed-off-by: Sacha Al Himdani <sacha@probo.com>
The @probo/eslint-plugin-relay-types shim only types an
`eslint-plugin-relay` import, which no app makes anymore now that
linting runs through the single root eslint.config.mjs. The apps'
tsconfig.node.json even pulled it in for a per-app eslint.config.mjs
that no longer exists.
Remove the devDependency and the `types` entry from each app's
node tsconfig, and drop the stale eslint.config.mjs include. The
shim stays for packages/eslint-config, its real consumer.
Signed-off-by: Émile Ré <emile@probo.com>
When an email link points to an employee signature or approval document
that has been deleted or is no longer accessible to the user, the detail
pages rendered an infinite spinner. Redirect to the signatures/approvals
tab list instead, both when the document resolves to null and when it has
no accessible versions.
Signed-off-by: Sacha Al Himdani <sacha@probo.com>
Requesting a signature only validated that the version was PUBLISHED, so a
signature could be requested on a superseded (older) published version. Reject
versions that are not the document's current published major/minor, and hide
the request button in the console for non-current versions.
Signed-off-by: Sacha Al Himdani <sacha@probo.com>
Update the compliance page alias field and list items to call the
generic setResourceAlias and removeResourceAlias mutations instead of
the trust-center-specific ones, and drop the now-removed audit alias
field usage.
Signed-off-by: Bryan Frimin <bryan@probo.com>
The audit alias resolver returned raw service errors. Log them
and return gqlutils.Internal like other resolvers in the file.
Remove-only users could edit the alias field to a new value that
was never saved. Reset local state when set permission is missing,
and catch mutation rejections on blur.
Sitemap generation appended audit report file IDs without
deduplication, which could emit duplicate document URLs when
multiple audits share the same report file.
Signed-off-by: Bryan Frimin <bryan@probo.com>