The OAuth2/OIDC server accepted its signing key via a file path
(key-file), while every other PEM key in the probod config (SAML
private key, ACME account key) is embedded inline. Switch the
field to a private-key string so the convention is uniform.
The signing key is operator-supplied material that must outlive
any process restart, so the bootstrap builder now treats
OAUTH2_SERVER_SIGNING_KEY as required and refuses to start
without one; silently minting a fresh key per boot would break
token validation across rollouts. The OAUTH2_SERVER_* env vars
otherwise flow through builder.Build like the existing SAML
block so the new OAuth2Server section is populated end-to-end.
Rework the e2e harness to render its config via bootstrap at
test setup, which removes the static
e2e/console/testdata/config.yaml and the previously generated
test-only PEM file. A per-run RSA key is minted via
bootstrap.GenerateOAuth2SigningKey (kept public for test
tooling) and injected through the builder env map. CI now
passes ACME_ROOT_CA inline instead of mutating a YAML on disk.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
Two bugs caused SCIM sync failures:
1. buildUserPayload conditionally omitted empty fields. When a field was
cleared in the identity provider, the PUT payload didn't include it,
so the SCIM handler never cleared the stored value. The bridge kept
detecting a mismatch every sync cycle, causing a perpetual PUT loop.
Fix: always include all fields unconditionally.
2. ListUsers ignored the startIndex parameter — the cursor always started
from nil, so every page returned the same first N users. Organizations
with more than 100 SCIM-managed users never got a full listing; users
beyond the first page appeared missing, causing CreateUser calls that
failed with 409 (uniqueness conflict) and eventually disabled the
bridge. Fix: replace cursor-based pagination with OFFSET/LIMIT to
honor SCIM's 1-based startIndex.
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
Address PR review comments: capture Exec result and check
RowsAffected() == 0 to return ErrResourceNotFound in
Cookie.Update, CookieCategory.Update, and CookieCategory.UpdateRank.
Also update coredata and relay contributor docs accordingly.
Signed-off-by: Émile Ré <emile@getprobo.com>
ConnectionHandler.getConnection could not find the cookies
connection because orderBy was implicitly treated as a filter.
Add filters: [] to the @connection directive and document the
filters rule in contrib/claude/relay.md.
Signed-off-by: Émile Ré <emile@getprobo.com>
The create, delete, and move cookie mutations were not updating
the Relay store connections, so the UI only reflected changes
after a page reload. Add @connection and @appendEdge/@deleteEdge
directives, and a store updater for the move mutation.
Also document mutation store update rules in contrib/claude/relay.md.
Signed-off-by: Émile Ré <emile@getprobo.com>
Move profile load before the owner-demotion guard and add
an active-state check, matching the RemoveUser pattern.
Without this, demoting an inactive owner would be
incorrectly blocked.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
The UpdateMembership path allowed the sole owner of an
organization to change their role to a non-owner role,
causing permanent lockout. Add the same active-owner count
guard already used in RemoveUser.
Closes#1071
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
langsmith <=0.5.18 leaks streamed LLM output past redaction controls
(GHSA-rr7j-v2q5-chgv). The transitive pin via @n8n/ai-utilities held
@langchain/classic at 1.0.5 which required langsmith ^0.3.64, blocking
the upgrade. Add npm overrides for @langchain/classic (^1.0.27) and
langsmith (^0.5.19) to resolve all three langsmith advisories.
The lockfile refresh bumps eslint-plugin-react-hooks from 7.0.1 to
7.1.0 which enables the react-hooks/refs and set-state-in-effect
rules. Fix the resulting lint errors:
- PDFPreview (console + trust): read currentPage through a ref synced
via useEffect, keeping the setState updater pure and ref access
outside of render
- MeasuresPage: remove redundant categoryFilter state that mirrored
urlCategory, use a ref to detect URL changes and trigger refetch
as the single source of category refetches
- WebhooksSettingsPage: defer loadEvents via requestAnimationFrame
to avoid synchronous setState within the effect body
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
Drop the boolean implemented/not-implemented state in favor of a
mandatory CMMI maturity level enum (NONE, INITIAL, MANAGED, DEFINED,
QUANTITATIVELY_MANAGED, OPTIMIZING) stored as a Postgres enum type.
The migration backfills existing rows (NOT_IMPLEMENTED → NONE,
IMPLEMENTED → INITIAL), makes the column NOT NULL, and drops the old
implemented column and its enum type.
- maturityLevel is required on CreateControlInput and non-nullable (!)
in the GraphQL schema
- CLI displays human-readable CMMI labels instead of raw enum tokens
- SOA table and published document use a single Maturity column in
place of the old Implemented + Maturity columns
- Remove ControlImplementationState type and all implemented references
across backend, frontend, CLI, MCP, n8n, and E2E tests
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
Adds an optional CMMI 0-5 maturity level field to Control to support
ISO 27001 clause 9.1 effectiveness measurement and HITRUST CSF maturity
requirements. The field is nullable, framework-agnostic, and exposed
across all four API surfaces (GraphQL, MCP, CLI, n8n) plus the
generated SoA document.
Signed-off-by: Alejandro Juan <alejandrojuan@alejandrojuan.com>
Moving a cookie between categories previously required two sequential
updateCookieCategory mutations, which was not atomic and could leave
data in an inconsistent state if the second call failed. This adds a
dedicated moveCookieToCategory mutation that performs both updates in
a single transaction.
Signed-off-by: Émile Ré <emile@getprobo.com>
Replace the `required` boolean column on cookie_categories with a `kind`
enum (NORMAL, NECESSARY, UNCATEGORISED). The Necessary category remains
undeletable and always-on for consent; the new Uncategorised category is
also undeletable but users can opt out of it.
When a category is deleted, its cookies are merged into the Uncategorised
category (lazy-created for legacy banners that don't have one yet).
Signed-off-by: Émile Ré <emile@getprobo.com>
Parallel gqlgen instances race on the Go build cache, causing
Rewriter.getSource() to panic with "slice bounds out of range"
when go/packages returns empty source. Chain gqlgen targets with
order-only prerequisites in the Makefile and switch the CI build
job to sequential generation.
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
Move the PageHeader into CookieBannersOverviewPage directly since
the layout had only one child route left after extracting the "new"
route as a sibling, making the wrapper unnecessary.
Signed-off-by: Émile Ré <emile@getprobo.com>
Category reordering previously required two separate
updateCookieCategory calls to swap ranks, which was not
atomic. Replace with a single reorderCookieCategory mutation
that shifts all affected ranks in one SQL statement, and
remove the rank field from UpdateCookieCategoryInput.
Signed-off-by: Émile Ré <emile@getprobo.com>
Replace generic names (commitMutation, commitCreate, isInFlight, etc.)
with names derived from the graphql tagged-template variable minus the
Mutation suffix. Add naming convention rule to contrib/claude/relay.md.
Signed-off-by: Émile Ré <emile@getprobo.com>
Drop meetings and meeting_attendees tables, remove all meeting-related
code across GraphQL, MCP, CLI, N8N, webhooks, frontend, and e2e tests.
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
Delete duplicate connectors per (organization_id, provider), keeping the
oldest one, then add a unique index to prevent future duplicates.
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
Mirror the SOA-to-document migration for the data list. Remove data
from the snapshot system and add a publish workflow that generates a
ProseMirror document for the full organization data inventory.
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
Expose document version approval quorums and decisions
through MCP, CLI, and n8n. This lets users inspect who
approved or rejected a document version, including the
rejection comment, without relying solely on the audit
log.
MCP tools: listDocumentVersionApprovalQuorums,
getDocumentVersionApprovalQuorum,
listDocumentVersionApprovalDecisions,
getDocumentVersionApprovalDecision.
CLI commands: document list-approval-quorums,
view-approval-quorum, list-approval-decisions,
view-approval-decision.
n8n operations: Get/Get Many Approval Quorums and
Approval Decisions on the Document resource.
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
Expose --probo-font-size and --probo-btn-radius so customers
can scale typography and button shape without overriding
internals. The type scale now uses calc() offsets from the
base variable. The settings button defaults now follow
--probo-accent so a single accent change updates both the
banner and the floating button.
Signed-off-by: Émile Ré <emile@getprobo.com>
Headless components are behavioral wrappers meant to be styled
by the consumer with regular CSS. Shadow DOM on each child
created unnecessary style boundaries with no visual content to
protect. Only ProboSettingsButton retains its own shadow root
since it renders encapsulated styled markup.
Also removes the auto-registerComponents() side effect from
the main entrypoint so consumers opt in explicitly.
Signed-off-by: Émile Ré <emile@getprobo.com>