Introduce dedicated employee-scoped IAM actions and update all
resolvers and frontend mutations accordingly.
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
Add classification as a filter-only field on documents, resolved from
the latest document version. Expose in GraphQL, MCP, and document list
UI with a selector alongside the document type filter.
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
Classification now lives exclusively on DocumentVersion. The field is
removed from the Document model, all SQL queries, GraphQL Document
type, SignableDocument type, UpdateDocumentInput, and MCP Document
schema.
New documents still accept classification in CreateDocumentInput,
applied to the first version. New drafts inherit classification from
the previous version. PDF generation uses the version classification.
The drawer allows editing classification on draft versions via the
updateDocumentVersion mutation. Classification is read-only on
published versions.
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
Add ISC license headers to all .go, .ts, .tsx, and .sql files
using each file's git history to determine the correct copyright
year or year range. Trademarked icons (brand logos, vendor logos,
compliance framework logos) are excluded.
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
Introduce a two-part version scheme (major.minor) for documents.
Drafts start at 0.1 and increment minor on each new draft. Publishing
as minor keeps the current version, publishing as major bumps to the
next major.0. Both current_published_major and current_published_minor
are tracked on the document for exact version lookups.
Signatures and approval quorums aggregate across all versions sharing
the same major number using CTE joins. Approval page mutations spread
the decision fragment so Relay updates the version row state without
requiring a page refresh.
GraphQL, MCP, and service layer expose separate publishMajor and
publishMinor mutations instead of a single mutation with a type enum.
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
Introduce a complete approval system for document publishing. Document
versions can now require approval from selected reviewers before being
published, with automatic publishing once all approvers have approved.
- Add approval quorum and decision tables with backfill migration
- Implement request approval, approve, and reject flows with electronic
signature support for approve decisions
- Add employee approvals page with dedicated tab and pending approvals view
- Add changelog field to publish and request approval flows
- Pre-select previous version's approvers in the publish dialog
- Show quorum approvers in document list with 100 approver hard limit
- Expose approval workflow through GraphQL, MCP, and CLI
- Remove legacy default approvers feature entirely
- Add comprehensive e2e test coverage for approval workflows
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
Introduce a rank-style priority on tasks, scoped by
(organization_id, state). New tasks auto-assign the next
priority. Reordering uses the same CTE-based algorithm as
trust center references and compliance external URLs.
Exposed through GraphQL, MCP, and the PRIORITY order field.
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
Previously the worker reset failed descriptions back to PENDING,
causing infinite retry loops for permanently broken evidence.
Add a FAILED status so these items stop cycling and can be
investigated.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
Introduce a background worker that automatically generates
compliance-focused descriptions for uploaded evidence files
using configurable LLM providers. Descriptions are surfaced
across all interfaces: GraphQL API, MCP API, CLI, and the
console UI.
Key changes:
- Multi-provider LLM config with per-agent settings (pointer
types for Temperature/MaxTokens to preserve zero values)
- Evidence description worker with bounded concurrency
- EvidenceDescriptionStatus typed enum with PostgreSQL enum type
- New `prb evidence` CLI commands (list, view, delete)
- Evidence description displayed in console table and preview
- Migration only marks evidences without files as completed
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
Trust centers were created without setting SearchEngineIndexing,
defaulting to an empty string in the database. This caused scan
errors when loading trust centers.
Add a migration to fix corrupted rows, set a DEFAULT on the
column, and add a CHECK constraint. Also set the field explicitly
when creating new trust centers.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
Introduce dataloadgen-based dataloaders to batch individual
record-by-ID fetches in GraphQL resolvers into single SQL
queries. Each entity type (organization, framework, control,
vendor, document, risk, measure, task, file, report, profile)
gets a LoadByIDs method in coredata and a GetByIDs service
method with variadic arguments and dedicated collection return
types. Resolvers now use dataloader.FromContext instead of
direct service calls for single-record lookups.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
Add search engine indexing toggle, robots.txt, and sitemap.xml
generation for compliance pages. Replace checkboxes with toggle
components in the compliance page UI and add an "Open" button
in the page header to quickly access the live compliance page.
The search engine indexing toggle is disabled when the compliance
page is inactive.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
The OIDC provider is already tracked in iam_oidc_states.provider, so
there is no need for provider-specific session auth methods (GOOGLE,
MICROSOFT). Replace them with a single OIDC auth method.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
Redesign the sign-in page to show email/password form inline
with OIDC provider buttons (with vendor icons) instead of
separate pages. Extract OIDCProvider type to its own file.
Replace errgroup with sync.WaitGroup + WithCancelCause for
graceful shutdown in IAM services. Refactor garbage collectors
to use functional options and time.Ticker instead of
time.After to avoid repeated allocations.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
Implements OpenID Connect authentication flow with PKCE, JWT verification, and enterprise-only account restrictions. Adds OIDC service with JWKS caching and state management, HTTP handlers for login/callback flows, GraphQL query for available providers, and sign-in UI integration.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
When an email has a sender name set (the organization name), the
mailer composes the From header as "OrgName via Probo" instead of
the default global sender name. This gives compliance page
recipients clearer context about which organization is contacting
them.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
Adds audit logging that records all authorized actions performed by
users and API keys. The audit log is automatically populated whenever
the authorizer approves an action, and is queryable via GraphQL, MCP,
and CLI interfaces. Permission checks are excluded via a dry-run flag
to avoid phantom entries on page loads.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
Add structured organization context with 5 markdown sections (Product, Architecture, Team, Processes, Customers) editable inline. Meetings are now a tab within the context page. Moved all GraphQL queries from hooks/graph/MeetingGraph.ts into colocated components following new best practices. Updated database schema, backend services, GraphQL resolvers, and MCP API to support the new context fields and structure.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
Documents can be archived and unarchived. Archived documents are
read-only, excluded from the trust center, and moved to a dedicated
Archived tab in the document list.
- Add archived_at timestamp and status (ACTIVE/ARCHIVED) PG enum column
- Rename DocumentStatus → DocumentVersionStatus, introduce DocumentStatus
- Archive/unarchive mutations in GraphQL, MCP, and CLI
- Bulk archive/unarchive mutations with Active/Archived tabs in the list
- ABAC policies: write actions denied on archived docs, unarchive denied
on active docs
- Remove control/risk mappings and reset trust center visibility on archive
- Exclude archived documents from mapping dialogs and trust center tab
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
The pg_advisory_xact_lock inside the WITH clause caused race conditions
in READ COMMITTED mode. When a transaction blocked on the lock and resumed
after acquiring it, it used a stale snapshot and computed the same reference
ID as the previous transaction, violating the unique constraint. Moving the
lock to a separate statement before the INSERT ensures the snapshot includes
all previously committed data.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
Adds 5 new document types (PLAN, REGISTER, RECORD, REPORT, TEMPLATE), renames ISMS to GOVERNANCE, and implements type-based filtering across GraphQL, MCP, and frontend. Includes migration, enum updates, filter implementation with SQL array support, and frontend dropdown UI with Relay refetch pattern.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
CountByRiskID and CountByControlID CTEs did not include m.category in their SELECT
lists, causing "column category does not exist" errors when MeasureFilter applied
category constraints. Added m.category to both CTEs.
Also add comprehensive e2e tests for measure filtering by category at organization,
risk, and control levels.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
The CountActiveOwnerByOrganizationID query joins iam_membership_profiles with
iam_memberships, but used an unqualified tenant_id = @tenant_id in the WHERE
clause. Since both tables have a tenant_id column, PostgreSQL raised an
"ambiguous column" error when deleting an owner. Fixed by prefixing with the
table alias (p.tenant_id) to match the pattern used in CountByOrganizationID.
Added TestUser_RemoveOwner e2e test to verify one owner can remove another.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
Support distinguishing between minor and major non-conformities in findings. Rename the existing NONCONFORMITY enum value to MINOR_NONCONFORMITY and add a new MAJOR_NONCONFORMITY value across all API layers (GraphQL, MCP, CLI) and the database.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
Replace the client-side grouped-by-category view (fetching 500 items)
with a flat table using server-side filtering and cursor-based
pagination. Colocate GraphQL queries, fragments, and mutations in the
component file per console CLAUDE.md conventions.
Backend changes add a category filter to the measure list endpoints
(GraphQL, MCP) and a new measureCategories field on Organization.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
Adds a visibility enum to files with PRIVATE (default) and PUBLIC states.
PUBLIC files are accessible via an unauthenticated /api/files/v1/{fileID}
endpoint that redirects to a presigned S3 URL. Introduces pkg/file service
to manage file operations. Logo uploads (trust centers, organizations,
frameworks, references) are marked PUBLIC; other files are PRIVATE.
Includes database migration and backfill for existing logos.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
Introduce the Finding, FindingAudit, FindingKind, FindingStatus,
FindingPriority, FindingFilter, and FindingOrderField types in the
coredata layer. Add CRUD operations, list with filtering/pagination,
and audit association queries.
Remove the now-replaced nonconformity and continual_improvement
coredata types. Update entity type registry and snapshot types
to reference the new findings type.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
Merge nonconformities and continual_improvements tables into a
unified findings table with kind (NONCONFORMITY, OBSERVATION,
EXCEPTION), status, and priority enums. Create findings_audits
junction table for the many-to-many audit relationship.
The migration generates new FND-XXX reference IDs per organization,
migrates both live and snapshot records preserving source_id links,
and carries over audit associations to the junction table.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
Introduce `implemented` enum (IMPLEMENTED/NOT_IMPLEMENTED) and
`not_implemented_justification` (nullable text) fields on the Control
entity across all API surfaces (GraphQL, MCP, CLI), database, frontend,
and SOA export.
The database stores implementation state as a PostgreSQL enum
`control_implementation_state`. Controls default to IMPLEMENTED during
migration. The SOA list and PDF export show implementation status
alongside applicability, with "-" for non-applicable controls.
Justification columns are renamed for clarity: "Justification for
non-applicability" and "Justification for non-implementation".
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
When a SCIM provider sends CreateUser for a user whose email matches an
existing manual profile, but another profile already holds that
external_id (e.g. created by a prior CreateUser with a different email),
clear the conflicting external_id before enrolling the manual profile.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
When a SCIM configuration is deleted, ResetSCIMSources now clears
external_id and user_name so stale values don't cause unique constraint
violations when SCIM is reconfigured.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>