Commit Graph

498 Commits

Author SHA1 Message Date
Émile Ré
eecf8a1d97 Make profile state filters multi-value
Address PR review feedback on the profile-state split: SAML sign-in now
activates a pending profile, deactivation counts owners against the
profile's own organization to close a last-owner bypass, the migration
leaves historical activated_at/deactivated_at NULL rather than
fabricating timestamps, and pending members are no longer rendered with
the deactivated (faded) styling.

Drop the single-value state filter in favor of the multi-value states
across the profile and signatory surfaces. Remove ProfileFilter.state
(only states[] remains) and convert the signatures profileState filter
to profileStates. Turn the console people filter, the CLI
"user list --state" flag, and the n8n listUsers and getAllSignatures
state inputs into multi-select controls, where an empty selection means
all states.

Signed-off-by: Émile Ré <emile@probo.com>
2026-07-30 09:19:42 +02:00
Émile Ré
4a276e3ef7 Split inactive profile state
Replace the binary profile ACTIVE/INACTIVE model with PENDING, ACTIVE,
and DEACTIVATED so invited-but-not-yet-activated members remain
assignable to assets, data, and risks instead of being treated like
deactivated users.

Add activated_at/deactivated_at timestamps and Mark* lifecycle helpers,
and update every transition (create, invite/re-invite, activation,
archive, SCIM, SAML, sessions, compliance-portal grant) to the new
states. Expose a multi-state states[] filter across coredata, GraphQL,
MCP, and the console owner pickers, which now request ACTIVE and
PENDING members.

A migration renames the membership_state enum, classifies existing
inactive profiles as PENDING from recent invitation activity, and
backfills the new timestamp columns.

Signed-off-by: Émile Ré <emile@probo.com>
2026-07-30 09:19:42 +02:00
Sacha Al Himdani
cd6c46212a Add log export for audit logs and SCIM events
Route audit-log and SCIM-event exports through export_jobs with typed
arguments, an iam BuildAndUploadExport/SendExportEmail implementation,
and a concurrent export-job worker with stale recovery. Stream JSONL via
page.WalkAll into S3, and expose the request flow on console, connect,
MCP, and CLI.

Co-authored-by: Bryan Frimin <bryan@getprobo.com>
Signed-off-by: Sacha Al Himdani <sacha@probo.com>
2026-07-29 18:57:31 +02:00
Cursor Agent
f451e94c0b Clear document mappings on soft delete
Document soft delete cleared generated-document references but left
control, risk, and measure junction rows pointing at deleted documents.
That blocked risk deletion and made unlinkRisk fail when the document
was already gone.

Remove entity mappings in SoftDelete and BulkSoftDelete, drop mappings
before deleting a risk, tolerate missing documents when unlinking, and
backfill orphaned junction rows for soft-deleted documents.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
2026-07-29 18:00:32 +02:00
Ludovic Vielle
ad615a47a0 Show posture values and report history
Pass/fail was the main device UI signal, but operators need
the agent's observed value. Expose a formatted value per
check, show current postures on the device page, and replace
the Postures tab with paginated report history grouped by
agent push time. Status stays in the model for later rulesets.

Signed-off-by: Ludovic Vielle <ludovic@probo.com>
2026-07-29 16:27:23 +02:00
Cursor Agent
c7b1562d26 Store audit engagement dates as DATE columns
Match valid_from and valid_until on audits and the console
date-only inputs for audit start and end.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
2026-07-28 17:27:34 +02:00
Cursor Agent
f8f5152566 Use timestamptz for audit engagement dates
Store audit_start_date and audit_end_date as TIMESTAMP WITH
TIME ZONE. Alphabetize n8n audit field collections to satisfy
n8n-node lint.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
2026-07-28 17:27:33 +02:00
Cursor Agent
fe215efbd1 Add audit start and end dates to audits
ISO audits often span a window distinct from certificate validity.
Store optional audit_start_date and audit_end_date on the audit
record and expose them through GraphQL, MCP, CLI, n8n, and console.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
2026-07-28 17:27:33 +02:00
Steven4Hooisma
e62e6cce43 Add UpCloud integration with account listing and details retrieval
Signed-off-by: Steven4Hooisma <112615049+Steven4Hooisma@users.noreply.github.com>
2026-07-27 11:08:24 +02:00
Aurélien Sibiril
20502fc0be Use the MIT license header on the new connector files
The four connectors landed with the ISC header. contrib/claude/license.md
mandates MIT for every source file, and these were the only ISC files
left in pkg/accessreview/drivers and pkg/connector/provider — the two
"Make license declarations consistently MIT" commits had already cleared
both packages, so this re-opened a closed cleanup.

Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
2026-07-26 09:22:05 +02:00
Aurélien Sibiril
60628645ae Add Google Analytics, Dotfile, Segment and Square access-review connectors
Two OAuth2 and two API-key connectors:

- Google Analytics (GA4): OAuth2 with both analytics.readonly and
  analytics.manage.users.readonly (readonly alone 403s on the accounts
  list); v1alpha accessBindings enumerated at account and property level
  and merged by email; manual account picker (Pattern 1) with a
  per-connection probe and name resolver; distinct from Google Workspace.
- Dotfile: API key in the X-DOTFILE-API-KEY header (Pattern 3); GET
  /v1/users (owner/admin, suspended_at) with a static probe.
- Segment (Twilio): Public API token as Bearer with a required Region
  setting (US or EU) mapped to the regional host; GET /users plus per-user
  GET /users/{id} for roles and /invites for pending members; per-connection
  BuildProbeURL.
- Square: OAuth2 (EMPLOYEES_READ) or a personal access token (Pattern 3);
  POST /v2/team-members/search returns email/status/is_owner directly, so no
  role resolution; custom probe and name resolver.

Google Analytics and Square are confidential OAuth clients, wired into the
bootstrap OAuth provider list and .env.example. Segment carries a required
extra setting, so the console add-source dialog maps region onto its
segmentRegion API-key input; without that mapping the value is silently
dropped and the create is rejected.

Cassette-backed driver tests plus unit tests for the Segment probe URL and
the bootstrap OAuth provider list.

Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
2026-07-26 09:22:04 +02:00
Ludovic Vielle
1f79453386 Add device data model and ITAM service
Introduce device, posture, and enrollment-token entities with ITAM
service policies for agent-managed fleet inventory.

Signed-off-by: Ludovic Vielle <ludovic@probo.com>
2026-07-24 15:08:21 +02:00
Émile Ré
0d832508c9 Replace portal title with entity name
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>
2026-07-22 13:08:02 +02:00
Émile Ré
418bb5a8f8 Address review feedback on portal i18n
Swallow locale mutation rejections after the toast, close the
mobile drawer on locale change, escape SEO paths, share the
IAM locale list with SEO, and finish dropping /trust leftovers.

Signed-off-by: Émile Ré <emile@probo.com>
2026-07-21 18:43:50 +02:00
Émile Ré
c223873e96 Put locale in compliance portal URLs
Path-segment locales make each language crawlable with self
canonical and hreflang, while identity.locale persists an
explicit choice without client storage or cookie banners.

Signed-off-by: Émile Ré <emile@probo.com>
2026-07-21 18:43:48 +02:00
Bryan Frimin
63dffe801e Store full portal titles as home headings
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>
2026-07-21 15:44:29 +02:00
Bryan Frimin
a3d6c120d7 Mint certificate GIDs with entity type 106
Certificate backfill migrations used entity type 104, which belongs to
commitment groups. Use 106 so migrated certificate IDs decode correctly.

Signed-off-by: Bryan Frimin <bryan@probo.com>
2026-07-21 15:44:28 +02:00
Bryan Frimin
43ce3a7c53 Harden compliance portal auth and TLS
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>
2026-07-21 15:44:22 +02:00
Bryan Frimin
7e0d187dcf Flatten compliance portal package layout
Remove the root complianceportal package and the resolver
facade that existed only to break an IAM import cycle. Admin
policies, domain URL helpers, and actions live under
management; visitor OAuth metadata, brand URLs, and public
read paths live under visitor. Drop the duplicate trust API
magic-link mutations now that Connect handles portal auth, and
stop IAM from owning compliance page email branding.

Signed-off-by: Bryan Frimin <bryan@probo.com>
2026-07-21 15:44:20 +02:00
Bryan Frimin
bc78f08334 Harden compliance portal data migrations
Mint certificate and domain GIDs with generate_gid, preserve
org profile and domain rows that lack a trust center, and skip
managed default-domain backfill when no base domain is set.

Signed-off-by: Bryan Frimin <bryan@probo.com>
2026-07-21 15:44:19 +02:00
Bryan Frimin
ec91106063 Add COMPLIANCE_PORTAL OIDC provider type
Register the new provider enum value so compliance portal OAuth
clients can be stored and resolved like other IAM OIDC providers.

Signed-off-by: Bryan Frimin <bryan@probo.com>
2026-07-21 15:44:10 +02:00
Bryan Frimin
ba53c94bdc Move trust center profile onto the page
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>
2026-07-21 15:44:10 +02:00
Bryan Frimin
55a8e72c17 Rename external URLs and move profile fields
Rename the compliance external URL concept to compliance custom links,
and move the public-facing profile fields (description, website, email,
headquarter address) off the organization onto the trust center. Backfill
managed default domains for pages that lack them.

Signed-off-by: Bryan Frimin <bryan@probo.com>
2026-07-21 15:43:08 +02:00
Bryan Frimin
7c5759733c Extract certificate lifecycle into certificates
Pull SSL/ACME state out of custom_domains into a dedicated hostname-keyed
certificates table, and repoint cached certificates from the domain to
the certificate. Custom domains now reference a certificate id, keeping
certificate provisioning decoupled from domain business logic.

Signed-off-by: Bryan Frimin <bryan@probo.com>
2026-07-21 15:43:07 +02:00
Bryan Frimin
75203acff2 Move custom domains onto trust centers
Custom domains previously hung off the organization. Attach them to the
trust center instead, adding default and custom domain references plus a
managed flag, and backfill existing rows. Domains now belong to a
compliance page rather than the whole organization.

Signed-off-by: Bryan Frimin <bryan@probo.com>
2026-07-21 15:43:07 +02:00
Cursor Agent
a3fac4f543 Add rights requests to webhooks
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>
2026-07-21 13:24:23 +02:00
Émile Ré
622f1ba67d Address PR review on data request pages
Require a verified viewer email before creating a rights request and
validate the free-text fields with the same SafeText bounds the console
uses, so this public portal mutation stays safe and bounded.

Move myRightsRequests onto the base Query, drop the now-dead count
loaders, and order the RECTIFICATION enum value before PORTABILITY so
the Postgres sort order matches RightsRequestTypes().

Harden the v2 kit primitives: SegmentedControl keeps equal-width cards
(auto-fill), preserves its selection when the active card is toggled,
and forwards an accessible name; Field associates its label and error
by id/aria instead of wrapping the control in a label. Give the type
group an accessible name, require the name field for non-complaint
types, use a timezone-stable reference year, drop the underreporting
header count, and neutralize the response-deadline copy.

Signed-off-by: Émile Ré <emile@probo.com>
2026-07-20 14:31:42 +02:00
Émile Ré
6623cbc6f2 Add data request pages to compliance portal
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>
2026-07-20 14:31:42 +02:00
Cursor Agent
d2286aa1ac Tear down workflows when archiving documents
Archive and bulk archive now void pending approval quorums and
cancel requested signatures before freezing the document. Unarchive
reverts any leftover pending approval to draft. A migration backfills
archived documents that still carry in-flight workflow rows.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
2026-07-20 12:13:41 +02:00
Émile Ré
e6dcb7ea00 Add commitment reordering with rank move buttons
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>
2026-07-16 15:07:29 +02:00
Émile Ré
0b146a4054 Add configurable compliance portal commitment cards
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>
2026-07-16 15:07:29 +02:00
Sacha Al Himdani
944bcb7380 Add updated-from entity snapshot to updated webhooks
Update webhook events now carry a top-level "updatedFrom" field
alongside "data", containing a full snapshot of the entity as it was
before the update. This lets subscribers diff old vs new state (for
example the prior membership role on user:updated) without tracking
prior state themselves. It is a complete snapshot with the same shape as
"data", not a partial diff, so consumers select whatever fields they
need. The field is omitted for non-update events.

The webhook_data table gains a nullable updated_from JSONB column, and
webhook.InsertUpdateData enqueues both snapshots; InsertData delegates to
it with a nil updatedFrom so non-update callers are unaffected. Each
*:updated emission site snapshots the entity right after load, before
mutation: obligation, third-party, user (org and SCIM flows), document,
document-version, and document-version-approval-quorum. The document
emit helpers gained an optional updatedFrom argument threaded through to
the payload.

For document-version-approval-quorum:updated the snapshot requires an
extra query, so it is now gated behind the same subscription-existence
check the emitter uses: when no subscriber is configured the load is
skipped entirely rather than running (and potentially failing the
approval) for an event nobody receives.

Add integration tests (against a real Postgres, skipped when none is
reachable) covering the updated_from round-trip, the SQL NULL behavior
when no snapshot is provided, and the no-op when no subscription matches,
plus a unit test asserting updatedFrom is omitted from the payload when
absent.

Document the new field in the probod and n8n changelogs and the n8n
README.

Signed-off-by: Sacha Al Himdani <sacha@probo.com>
2026-07-15 13:59:41 +02:00
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
Aurélien Sibiril
b408aab59d Add Scaleway, Yousign, Railway and Crisp access-review connectors
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>
2026-07-10 18:29:54 +02:00
Sacha Al Himdani
1df4af4556 Add document webhook events
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>
2026-07-01 17:13:59 +02:00
Cursor Agent
79285d97df Remove access review framework controls
Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
2026-06-30 14:11:09 +02:00
Sacha Al Himdani
f7e93cdec5 Migrate Slack sender to kit worker
Replace the custom polling loop in the Slack sender with the
go.gearno.de/kit/worker framework, matching the email sending worker.
Add a processing_started_at column to slack_messages so claims are safe
under the worker's claim/process model, with stale recovery for crashed
in-flight sends. Rename sender.go to worker.go to match the kit worker
file naming convention.

Signed-off-by: Sacha Al Himdani <sacha@probo.com>
2026-06-29 14:40:24 +02:00
Aurélien Sibiril
a0d3806c21 Add four API-key access-review connectors
Add Pylon, OpenRouter, incident.io and Brevo as access-review connectors.
All are API-key, single-tenant providers (Pattern 3): the key identifies
one tenant, so there is no OAuth flow, picker UI, or bootstrap/helm
configuration.

- Pylon: Bearer token, GET /users; resolves each user's opaque role_id to
  a role name via GET /user-roles, with cursor pagination.
- OpenRouter: Bearer management key, GET /api/v1/organization/members. The
  endpoint requires an organization account -- a personal key authenticates
  but returns 404 -- so the connection probe rejects 404 on top of 401/403
  (doProbeRequest gained an opt-in extra-reject set) to surface a non-org
  key at connect time instead of mid-campaign.
- incident.io: Bearer token, GET /v2/users. Its OAuth is outbound-only, so
  the API key is the inbound path; live base_role/custom_roles take
  precedence over the deprecated role enum.
- Brevo: API key in the api-key header (Registration.APIKeyHeader), GET
  /v3/organization/invited/users. A live recording corrected the documented
  schema: is_owner is a JSON boolean (not a string) and an id field is
  present, so it is used as the stable ExternalID.

The OpenRouter and Brevo cassettes are anonymized live recordings; Pylon
and incident.io use hand-authored fixtures (no self-serve test tenant). The
shared three-valued active-status mapping is consolidated into
activeFromStatus in driver.go.

Each adds the enum value, migration, GraphQL binding, provider
Registration, a driver with a cassette-driven test, and a brand logo.

Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
2026-06-24 22:22:51 +02:00
Aurélien Sibiril
6a285a59b9 Add five API-key access-review connectors
Add Mercury, Apollo.io, Deepgram, ClickHouse Cloud, and Langfuse as
access-review connectors. All are API-key, single-tenant providers
(Pattern 3): the key identifies one tenant, so there is no OAuth flow,
picker UI, or bootstrap/helm configuration.

- Mercury: Bearer token, GET /api/v1/users, cursor pagination.
- Apollo.io: x-api-key header, GET /api/v1/users/search (teammates).
- Deepgram: Token scheme; lists members across every project and
  dedupes by member_id, unioning per-project scopes.
- ClickHouse Cloud: HTTP Basic (keyId:keySecret); discovers the org
  via GET /v1/organizations, then lists its members.
- Langfuse: HTTP Basic (publicKey:secretKey); a base-URL setting
  selects the regional cloud host or a self-hosted instance.

Each adds the enum value, migration, GraphQL binding, provider
Registration, a driver with a cassette-driven test, and a brand logo.

Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
2026-06-23 19:21:13 +02:00
Ludovic Vielle
20b9321de3 Add read actions to all unprefixed scopes
Signed-off-by: Ludovic Vielle <ludovic@probo.com>
2026-06-23 18:24:50 +02:00
Bryan Frimin
2b8f0618de Generalize alias data layer to ResourceAlias
The trust_center_aliases table only allowed aliasing trust center
resources. Rename it to resource_aliases and scope uniqueness by
tenant rather than organization so any resource can carry an alias.
Replace the TrustCenterAlias coredata type with a ResourceAlias type
that loads and upserts aliases by resource ID.

Signed-off-by: Bryan Frimin <bryan@probo.com>
2026-06-22 11:38:51 +02:00
Bryan Frimin
ac5ee73997 Add trust_center_aliases table migration
Signed-off-by: Bryan Frimin <bryan@probo.com>
2026-06-22 11:38:48 +02:00
Bryan Frimin
5b0d3e5052 Add OAuth2 Client ID Metadata Document support
MCP connectors such as ChatGPT and Claude register via HTTPS
client_id URLs instead of pre-provisioned GIDs. Fetch and cache
their metadata documents, upsert clients on first use, and
advertise CIMD in OIDC discovery when allowed URLs are configured.

Signed-off-by: Bryan Frimin <bryan@probo.com>
2026-06-19 17:13:37 +02:00
Ludovic Vielle
6e6a093300 Add v1:iam:read scope to auditor mode
Signed-off-by: Ludovic Vielle <ludovic@probo.com>
2026-06-19 10:05:20 +02:00
Ludovic Vielle
fd2e0903ee Register API scopes on prb CLI OAuth client
Device logins only requested OIDC scopes while the authorizer now
gates API calls on v1:* scopes. Register the full scope set on the
well-known prb client, request it at login via CLIClientScopes, and
cover the device flow in e2e.

Collapse API scopes under an accordion on the consent screen and
document scope sync for future namespace additions.

Signed-off-by: Ludovic Vielle <ludovic@probo.com>
2026-06-18 20:11:55 +02:00
Ludovic Vielle
26c5002932 Add identity-scoped OAuth token management
Let users create, list, and revoke manual bearer tokens from
/me/oauth-tokens, scoped to their identity rather than an
organization. Manual tokens store a null client_id and are
authorized with a self-manage IAM policy.

Wire Connect GraphQL on Identity (list, create, revoke), add
console UI with scoped create flow and credentials dialog, and
cover the flow in e2e tests. Fix list pagination ordering and
keep the Relay connection in sync after create.

Signed-off-by: Ludovic Vielle <ludovic@probo.com>
2026-06-18 20:08:49 +02:00
Ludovic Vielle
e20f1de58a Backfill OAuth2 access token API scopes
OAuth grant tokens issued before API scope enforcement only stored
OIDC scopes. Set every existing iam_oauth2_access_tokens row to the
full supported scope set so bearer tokens keep working under the
OAuth2 scope gate.

Signed-off-by: Ludovic Vielle <ludovic@probo.com>
2026-06-18 19:07:28 +02:00
Émile Ré
7723b33aec Add first-party verdict and guards to tracker mapping
The tracker-pattern catalog was binary (linked to a vendor or not), so
generic and first-party artifacts (loglevel keys, wallet-extension keys,
an org's own trackers) were retried forever and, once one row was wrongly
attributed, re-propagated to every organization with no re-check.

Give catalog rows a terminal attribution verdict (UNDETERMINED,
THIRD_PARTY, FIRST_PARTY): FIRST_PARTY short-circuits the whole mapping
pipeline so the artifact is never attributed again. Gate deterministic
vendor adoption behind a trust bar so only curated/operator rows
auto-propagate; lower-confidence agent/heuristic rows are reused as hints
and re-resolved, and an independent agent re-confirmation corroborates and
promotes them. Make the mapping agent emit an evidence source and reject
any attribution that lacks concrete evidence, and let it declare a
first-party verdict. Skip the speculative agent for PRE_EXISTING-source
patterns, whose low signal invites invented vendors.

Add proboctl "ctp mark-first-party" and an --attribution list filter to
audit and remediate existing wrong links, and a cursor rule documenting
migration naming so the timestamp is taken from date -u, not invented.

Signed-off-by: Émile Ré <emile@probo.com>
2026-06-18 17:23:37 +02:00
Sacha Al Himdani
f462b124e6 Batch signature and approval notifications via debounced worker
Replace the immediate per-document approval email and the manual
"send signing notifications" action with a single debounced worker that
batches pending requests per recipient and organization.

The worker (go.gearno.de/kit/worker) polls on an interval (default 5m)
and claims one (organization, recipient) group at a time, sending one
consolidated signing email and/or one approval email per recipient/org
that lists every document awaiting their signature or approval. The
claim is a conditional UPDATE that doubles as concurrency-safe dedup, so
several workers never email the same group twice.

Each request is notified once it has been pending past the debounce
delay (default 15m), then reminded at 1x, 2x and 3x the reminder
interval (default 1 day) after the previous email, after which it stops.
New last_notified_at and notification_count columns on signatures and
approval decisions drive the debounce, the widening reminder cadence and
the four-email cap.

Email copy lists each document with its title, type and a deep link to
the employee page. Removed the inline approval-on-publish email, the
SendSigningNotifications service method/mutation/MCP tool, its IAM action,
and the related console UI and n8n operation.

Signed-off-by: Sacha Al Himdani <sacha@probo.com>
2026-06-18 15:39:04 +02:00
Bryan Frimin
2c8ae26ea1 Open OIDC child session when assuming organization
OIDC login dropped organization_id before the provider redirect, so
callbacks with an existing matching root session never created an org
child session. Persist organization_id in OIDC state, open the child
session on callback, and forward the parameter from the sign-in UI.

Signed-off-by: Bryan Frimin <bryan@probo.com>
2026-06-18 12:11:01 +02:00