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>
Addresses PR review comments: clears selected document IDs when changing
the type filter to prevent bulk actions on hidden rows, updates the Relay
connection ID when the filter changes so mutations target the correct
connection, and fixes eslint indentation violations.
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>
Increase sleep time from 10ms to 1100ms to ensure timestamp precision works
consistently across databases with second-level granularity. Strengthen
AssertTimestampsOnUpdate to require strictly increasing timestamps instead of
allowing equal values.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
When creating an organization, the owner's membership profile was being set with the organization name as the full name instead of the owner's actual full name. Fetch the identity's full name and use it for the profile.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
Enforce minimum (25) and maximum (100) bounds on cursor size to prevent negative values or excessively large page sizes. All three API surfaces (GraphQL, MCP, CLI) funnel through NewCursor, so the fix belongs here.
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>
Remove createPortal and main element ref/state in favor of a simple
fixed overlay with top-12 offset. This avoids React 19 strict mode
violations around ref access during render and setState in effects.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
Replace mainRef with useState to avoid accessing ref.current during
render, which is forbidden by React 19's strict ref rules.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
Add cleanup function to the useEffect that sets position: relative on
the main element, so the style is restored when the component unmounts.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
Clamp the counter with Math.max(0, ...) and use <= 0 check to prevent
the drag overlay from getting stuck when dragLeave fires more than
dragEnter.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
Offset the fixed overlay with top-12 so the navigation bar remains
visible and unobstructed during file drag.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
The previous approach attached drag handlers to a content div that
didn't fill the viewport, making the dropzone hard to target. Now
drag detection uses window-level events with a counter for correct
nested element handling, and the full-screen overlay itself becomes
the react-dropzone drop target.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
- Move DialogFooter before type DialogRef to satisfy import-x/order
- Check errors argument in onCompleted callback to avoid treating
GraphQL errors as successful uploads
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
- Fix import order for DialogRef type
- Add explicit type parameter to useMutation
- Remove unnecessary parens around single arrow function arg
- Fix jsx-one-expression-per-line for MB text
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
Move uploadAuditReportMutation into CreateAuditDialog and replace
promisifyMutation with a Promise wrapper using native Relay callbacks.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
Enable users to drag-and-drop PDF reports onto the audit list page, which automatically opens the create-audit dialog with the file attached. The dialog chains createAudit → uploadAuditReport mutations, with graceful handling for partial failures (audit created but upload failed).
Changes:
- AuditsPage: Add dropzone with visual overlay (dashed border + icon) when dragging PDFs
- CreateAuditDialog: Accept optional file prop, show file info, chain mutations on submit
- Extract audit ID from createAudit response to pass to uploadAuditReport
- Handle upload failure with warning toast, allowing manual upload from audit detail page
- Add react-dropzone dependency to console app
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
Use the trust center's logo as favicon via the public file API URL,
falling back to the default favicon when no logo is configured.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
Render dynamic file content into a bytes.Buffer first so that
renderer errors return 500 instead of a partial 200 response.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
Implement server-side rendering of trust center page `<head>` with dynamic organization name and OG meta tags. Adds generic `FileRenderer` mechanism to statichandler for dynamic file rendering, allowing the trust server to inject templated content at request time.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
Implements native Go 1.26 cross-origin protection to block state-changing cross-origin browser requests. Registers configured AllowedOrigins as trusted origins and wraps the API router to check all incoming requests. Non-browser clients (MCP, Slack webhooks) are unaffected as they lack the browser-only Sec-Fetch-Site header.
Signed-off-by: gearnode <gearnode@probo.inc>
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
- Hold SELECT FOR UPDATE lock within transaction by using tx directly instead of separate WithConn, ensuring mutual exclusion when multiple requests race to verify the same token
- Fix "resouce" → "resource" typo in authentication error messages
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
Lima auto-forwards ports from the VM to localhost, which blocks local
development on the same ports (e.g. localhost:8080). Services are
already accessible via the VM IP shown by `sandbox.sh status`.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
The provision script relied on LIMA_CIDATA_USER being set in the
environment, but during cloud-init this variable is not exported — it
only exists in /mnt/lima-cidata/lima.env. The file cannot be sourced
directly because values like LIMA_CIDATA_COMMENT contain unquoted
spaces, so we extract LIMA_CIDATA_USER with sed instead.
Also ensure HOME is set for root's go install commands and explicitly
set PATH when running make as the Lima user via su, since profile.d
scripts may not be loaded during cloud-init provisioning.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
Fixes docker permissions by enabling Docker daemon during provisioning. Replaces make dev with three managed systemd services: probo-stack (auto-starting Docker Compose infra), probod (API server with gow for hot-reload), and probo-console (frontend dev server). The stack now starts automatically on VM boot; probod and console are started manually after build.
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>
When PROBOD_ENCRYPTION_KEY is set, always run probod-bootstrap to regenerate
the config file. This ensures that updated environment variables take effect
even when a stale config file exists on a persistent volume (e.g., PVC).
Previously, an existing config file would be reused unconditionally, causing
env var changes to be ignored on container restart.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
URI schemes are case-insensitive per RFC 3986, so HTTP:// and HTTPS://
must also be recognized when checking for existing schemes.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
The browse command and config loading were incorrectly prepending https:// to hosts that already had a scheme, resulting in malformed URLs like https://http://localhost:8080. Added normalizeHost() function to strip URL schemes when loading config, and added scheme detection in the browse command before prepending https://.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
Replace gopkg.in/yaml.v3 with sigs.k8s.io/yaml in write_test.go to match
the marshaling library used by WriteConfig. The sigs.k8s.io/yaml library
uses JSON struct tags, enabling proper round-trip serialization of configs
with custom unmarshaling logic like ConnectorConfig.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
Replace three instances of leftover %T format verbs in logger.ErrorCtx() calls with proper structured logging fields. Add alphabetically-sorted reference documentation for six new contrib/claude/ guides and reorder the existing list.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
Add a make target for probod-bootstrap and integrate it into the sandbox provisioning workflow. During VM provisioning, generate /etc/probod/config.yml with the Lima VM IP as the cookie domain, secure=false for HTTP access, and correct CORS origins. Also generate .env files for console and trust apps pointing to the VM IP. Update sandbox documentation to explain the auto-generated configuration.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
Update relay agent rules to mark both helpers as deprecated.
Replace examples with the preferred pattern: useMutation with
onCompleted/onError callbacks and useToast.
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
All panic(fmt.Errorf(...)) calls in the console and trust center
GraphQL resolvers are replaced with structured error logging via
r.logger.ErrorCtx and gqlutils.Internal(ctx) returns.
Mutation resolvers for Create, Update, Upload, Import, and Assess
operations now check for validator.ValidationErrors before returning
an internal error, surfacing field-level INVALID errors to clients
via gqlutils.InvalidValidationErrors.
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
Expand mixed inline/multiline function calls so each argument
is on its own line, matching the one-argument-per-line rule.
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
When an audit has no report attached, neither the View nor Request access button should be displayed. Updated the conditional rendering to wrap the entire button section with the audit.report check instead of only checking it in the nested ternary.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>