Replace the FileSign field in api.Config and server.Config with File
(*file.Service). Pass the new file.Service and other required deps to
files_v1.NewMux. Remove the now-redundant filesign package. Update
the favicon URL path to /api/files/v1/public/ in server.go.
Signed-off-by: Ludovic Vielle <ludovic@probo.com>
Add GET /public/{fileID} (unauthenticated, public files only) and
GET /{fileID} (session+API key+OAuth2, all files, core:file:get IAM
check). IAM and not-found errors both return 404 to prevent leaking
whether a file exists.
Signed-off-by: Ludovic Vielle <ludovic@probo.com>
The two actions expressed the same permission. Consolidate on
core:file:get and remove the now-redundant core:file:download-url
constant, policy entries, and all three call sites.
Signed-off-by: Ludovic Vielle <ludovic@probo.com>
Replace the inline PresignGetObject logic in probo.FileService with a
call to fileManager.GenerateFileUrl. Update the two callers.
Signed-off-by: Ludovic Vielle <ludovic@probo.com>
Add GeneratePublicPresignedURL and GeneratePresignedURL methods to
pkg/file. Update the GenerateFileURL path to /api/files/v1/public/
to reflect the upcoming endpoint rename. Pass fileManagerService to
file.NewService in probod.
Signed-off-by: Ludovic Vielle <ludovic@probo.com>
Move connectorProviderInfos from Organization to a new root query field
accessReviewDrivers, backed by a deployment-scoped policy so any
authenticated identity can list it without an org-scoped permission check.
Delete the now-unused helper file and update the frontend and e2e tests.
Signed-off-by: Bryan Frimin <bryan@probo.com>
Better Stack exposes team members and pending invitations through its
Uptime API. Wire it as an access-review connector so a Better Stack
team can be reviewed in access-review campaigns.
Better Stack has no third-party OAuth app for listing members (its
OAuth is an end-user MCP sign-in), so the connector authenticates with
a Bearer API token plus the team name that scopes the team-members
listing. The driver paginates /api/v2/team-members, maps roles and
invitation records into account records, and the source name is
resolved from the configured team.
This wires the full surface: the provider enum and migration, the
connector settings, the registry registration with the team-name extra
setting, the GraphQL input and resolver marshaling, the frontend field
mapping and connector logo, and cassette-backed driver tests.
Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
NewCursorFromFlags validated --first/--last, --after/--before, and
--after/--last as mutually exclusive, but missing the symmetric
--first/--before guard. Passing --first with --before fell into the
backward branch, where size defaulted from the unset --last and the
direction silently flipped to Tail, discarding --first and reversing
pagination. Reject the combination up front like the other invalid
pairings.
Signed-off-by: Émile Ré <emile@probo.com>
RegenerateCookieBannerTrackerPolicy authorized against the generic
cookie-banner update action, conflating policy regeneration with banner
edits. Add a dedicated regenerate-policy action and authorize both the
console and MCP resolvers against it so the capability can be granted
independently.
Signed-off-by: Émile Ré <emile@probo.com>
Replace the limit-driven auto-walking Paginate helper with explicit
cursor-pagination flags (--first/--after, --last/--before) that mirror
the GraphQL connection arguments. List commands now return a single
keyset page with its page info, and emit cursors so callers can page
forward and backward. --before no longer requires --last: both --first
and --last default to 50 when omitted.
Also split the tracker-pattern stats into enriched with and without a
description so the enrichment backlog is visible at a glance.
Signed-off-by: Émile Ré <emile@probo.com>
The common tracker pattern show command swallowed every error from
loading the linked common third party, hiding real database failures
behind silent partial output. Only tolerate ErrResourceNotFound now and
wrap any other error so the command fails loudly.
In the console, the copy-to-clipboard button reported success
unconditionally. Await the clipboard write and surface an error toast
when it rejects, guarding against a missing common tracker id.
Signed-off-by: Émile Ré <emile@probo.com>
Drop the --org mode and the --banner flag: the command now takes the
banner GID as a positional argument and resets exactly one banner. The
org-wide fan-out added operational risk for a destructive action without
a clear use case, and a positional GID reads more naturally than a flag.
Signed-off-by: Émile Ré <emile@probo.com>
The --reset-enriched flag was effectively a no-op: the enrichment
worker claims rows solely on enrichment_requested_at, and SetEnriched
rewrites enriched_at regardless, so clearing it never changed whether a
row was re-processed. Remove the flag and the resetEnriched parameter on
RequestEnrichmentByIDs, which now only stamps enrichment_requested_at.
Add a --without-description filter to the list and reenrich commands,
backed by a new described predicate on CommonTrackerPatternFilter, so an
operator can target catalog rows that still lack a description.
Signed-off-by: Émile Ré <emile@probo.com>
Require exactly one selection anchor (--id, --linked-banner,
--linked-org, or --common-third-party) for common-tracker-pattern
reenrich, dropping the catch-all --all; the tracker-type, keyword, and
state flags now narrow the anchor's result except when explicit --id
values are given. Add --linked-banner, --linked-org, and a tri-state
--with-common-third-party to the list command, replacing the separate
--linked/--unlinked booleans, and rename --third-party to
--common-third-party across both commands.
Support these by adding an ID restriction to CommonTrackerPatternFilter
so linked-banner/linked-org selections can be intersected with the
remaining filters in a single query.
Memoize the pg client on the proboctl Factory to avoid a duplicate
Prometheus collector registration panic when more than one command
path builds a client. Surface timestamps in both listing tables and
flag enriched-but-undescribed rows in the displayed enrichment state.
Signed-off-by: Émile Ré <emile@probo.com>
The tracker-mapping, common-pattern enrichment, and third-party
disambiguation agents were all built from one shared
TrackerAgentsConfig fed by a single tracker-mapping config slot. That
forced a single AgentTimeout to be reused and patched per worker, and
two unrelated max-turns fields to share one struct.
Split the in-code config into TrackerMappingAgentConfig,
TrackerEnrichmentAgentConfig, and DisambiguationAgentConfig, each with
its own timeout and max-turns, and add dedicated tracker-enrichment and
third-party-disambiguation provider slots (the latter resolving next to
third-party-vetter). Enrichment and disambiguation fall back to the
tracker-mapping slot when their own provider is unset, preserving
single-config deployments.
Drop the shared pkg/agentsbuild package and duplicate its small wiring
into probod and proboctl so the two executables stay decoupled. Wire
the new env vars, builder test coverage, and Helm values.
Signed-off-by: Émile Ré <emile@probo.com>
Expose a non-destructive re-trigger that re-arms tracker policy
generation for a banner that already has a published version, so the
tracker-policy worker regenerates the document after iterating on the
generator. RegenerateTrackerPolicy returns a conflict when nothing has
been published yet.
Wire it across all API surfaces per the api-surface rule: the console
GraphQL mutation and resolver, the MCP tool, the prb cookie-banner
regenerate-policy command, and the n8n operation, with console e2e
coverage for the success and no-published-version paths.
Signed-off-by: Émile Ré <emile@probo.com>
Add operator commands to proboctl for iterating on the cookie-banner
agents.
The global catalog groups (common-tracker-pattern, common-third-party)
list/filter/sort/show the catalogs using the shared coredata cursor
layer, and common-tracker-pattern reenrich re-describes selected rows by
running the enricher in-process (so it completes synchronously rather
than racing the async queue); a --cfg-file flag reuses probod's config
to wire the agent. --linked-banner/--linked-org target exactly the
catalog rows a banner or org depends on.
The cookie-banner reset-trackers command is tenant-scoped (it derives a
coredata.Scope from the banner/org GID) and rebuilds a banner's
uncategorised, non-excluded patterns from detected_trackers, decomposing
derived globs back into exacts, then re-arms the analysis and mapping
workers. --mapping-only skips the rebuild. A DB-backed test covers the
rebuild, link clearing, and preservation of categorised/excluded
patterns.
Signed-off-by: Émile Ré <emile@probo.com>
Introduce an API-style data layer for the global common tracker pattern
and common third party catalogs: typed filters, order fields, CursorKey,
cursor-paginated Load and CountAll, plus by-id enrichment re-queue and a
scoped reset/remap helper for a banner's tracker patterns. These reuse
the same page.Cursor/filter/order types the GraphQL API consumes, so a
future proboctl API can back them unchanged.
Extract the common-pattern enrichment logic out of the worker into a
CommonPatternEnricher service so it can run either from the background
queue or synchronously over a known set of ids; the worker becomes a
thin poller that delegates to it.
Extract the LLM client and tracker-agents config wiring into
pkg/agentsbuild so probod and other binaries build agents identically;
probod now delegates to it.
Signed-off-by: Émile Ré <emile@probo.com>
Expose the existing tracker_patterns.common_tracker_pattern_id foreign
key on the TrackerPattern type so it is possible to tell whether a
pattern is linked to the global common-tracker catalog (its description
likely came from the seed or the mapping/enrichment agents) or has no
link (added manually or inherited). This is a read-only debugging aid
for agent-generated descriptions; no migration or write path changes.
The field is added in sync across all four API surfaces (GraphQL, MCP,
CLI, n8n) plus the console UI, and covered by e2e assertions for both
the linked and unlinked cases.
Signed-off-by: Émile Ré <emile@probo.com>
Add a SigNoz connector so its organization members can be pulled into
access-review campaign snapshots. SigNoz authenticates with a
SIGNOZ-API-KEY admin service-account key and a customer-supplied base
URL (a SigNoz Cloud region/tenant host or a self-hosted instance).
The driver lists users via GET /api/v1/user, which returns the role
(ADMIN/EDITOR/VIEWER) inline so admin detection works in a single call,
and maps the SigNoz user status (active / pending_invite / deleted) to
the account active flag. The name resolver reads the organization
display name from GET /api/v2/orgs/me to title the access source.
Wire the provider through the coredata enum and settings, the
connector-provider registry (driver and name-resolver factories), the
console API-key input schema and validation, the access-review source
label, and the SigNoz brand logo.
Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
Introduce RiskAssessmentBoundary as a first-class, self-nesting entity that
groups nodes within a risk assessment scope, and thread it through every
surface.
- coredata: new risk_assessment_boundaries table + migration, boundary_id on
nodes, self-referential parent_boundary_id, entity type registration
- riskmanagement: boundary CRUD service methods, boundary_id wiring on node
create/update, scope-membership and self-parent validation, nested-subgraph
Mermaid rendering
- IAM: core:risk-assessment-boundary:{get,list,create,update,delete} actions
and viewer/auditor read policies
- console GraphQL: RiskAssessmentBoundary type, connection, order enum, CRUD
mutations, boundaries field on scope, boundaryId on nodes
- CLI: risk-assessment boundary command group and --boundary-id on nodes
- MCP: boundary tools and boundary_id on node tools
- n8n: boundary operations and boundary fields on node operations
- console UI: boundary list/create/edit, boundary selector on nodes, diagram
refetch on boundary changes
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
The pattern-analysis tokenizer split names only on underscore first,
then on dash. A key like "name_done:<uuid>" left the colon glued to
the leading hex group, so the trailing UUID was never recognised as a
single token and instead shredded on dash into short hex anchors. The
derived template kept those anchors fixed, producing a distinct glob
per UUID and preventing any merge.
Treat colon and dot as primary separators alongside underscore so the
embedded UUID is isolated, matched by isUUIDShape, and collapsed to a
wildcard. Extend templateHasFixedAnchor to ignore the new separators
so a separator-only template stays rejected by the anti-overmerge
guard.
Signed-off-by: Émile Ré <emile@probo.com>
The intermediate migration added lease_generation only for the next
migration to remove it. Fold the drops into 20260608T090000Z with IF
EXISTS so fresh installs and databases that already ran 20260607 both
migrate cleanly.
Signed-off-by: Bryan Frimin <bryan@probo.com>
Replace the single-case switch in the agent-run handler with an if,
and add the blank lines wsl_v5 requires around statements in the
approval checkpoint, worker tests, and policy-set registration.
Signed-off-by: Bryan Frimin <bryan@probo.com>
Drop the isType helper that merely discarded the value already
returned by errors.AsType and inline the suspend and interrupt
checks directly into the result-handling branch.
Signed-off-by: Bryan Frimin <bryan@probo.com>
Drop the lease, heartbeat, and stale-recovery test coverage that no
longer applies, and add coverage for the new behaviour: graceful
suspend returning a run to PENDING, an approval interruption parking it
in AWAITING_APPROVAL, and SubmitApproval merging decisions and
requeuing the run. Exercise the submitAgentRunApproval mutation end to
end, including the authorization paths for the relocated policies.
Signed-off-by: Bryan Frimin <bryan@probo.com>
The worker leaned on a lease plus a heartbeat goroutine and a stale
recovery sweep to reclaim runs from crashed workers. That machinery
raced with long LLM and tool calls and conflated graceful stops with
failures. Remove the lease columns, heartbeat, and stale recovery, and
rely on FOR UPDATE SKIP LOCKED for single-claim plus explicit state
transitions: a graceful suspend returns the run to PENDING and a crash
now leaves it RUNNING for manual recovery.
Treat an approval interruption as a known stop that parks the run in
AWAITING_APPROVAL, and add SubmitApproval to merge human decisions into
the checkpoint and requeue the run to PENDING. The decisions must cover
exactly the pending approvals, since a missing one would resume as an
implicit denial. Expose this through the submitAgentRunApproval
mutation.
Signed-off-by: Bryan Frimin <bryan@probo.com>
The agent-run actions and policies lived in the core probo policy set,
which forced every authorization change for the agent-run domain to
touch unrelated core files. Move the actions and the OWNER/ADMIN and
VIEWER/AUDITOR policies into the agentrun package and have it expose a
PolicySet that probod registers into the authorizer at composition
time, so the rules live alongside the domain logic they govern.
Signed-off-by: Bryan Frimin <bryan@probo.com>
The nested-restore test only checked that an unresolvable inner agent
left its tool call in the checkpoint for a later retry. It did not
verify what happens to a sibling inner agent that does resolve and
complete during the same restore.
Add a resolvable done-agent alongside the missing inner-agent and
assert that its progress is persisted: its inner checkpoint is dropped
and its result recorded as a completed call, so a later retry replays
only the still-unresolved branch instead of re-running finished work.
Signed-off-by: Bryan Frimin <bryan@probo.com>
The single- and multi-level suspend/restore tests gated their slow
leaf tool on a manual release channel closed 50ms after cancel().
That sleep was a guess at how long the suspend signal takes to reach
the running sub-agent, so the post-tool turn boundary could observe
the release before cancellation and complete the run instead of
checkpointing, making the assertions timing-dependent.
Expose the per-run suspend signal through SuspendSignalFrom in an
export_test shim and have the leaf tools block on it directly. The
tool now returns only once the graceful-suspend signal has actually
propagated to its agent, so suspension is observed deterministically
without sleeps or release channels.
Signed-off-by: Bryan Frimin <bryan@probo.com>
Each package that exercises the database against a real Postgres
carried its own copy of the connection bootstrap and schema setup.
Those copies had already drifted: some keyed off PROBO_TEST_PG_ADDR
with hardcoded defaults, others off PROBO_TEST_PG_URL, and the
agentrun/coredata suites hand-applied individual agent_runs
migrations to ensure the table existed.
Introduce a single test.PGClient helper that parses PROBO_TEST_PG_URL
(falling back to the local compose database), runs the full coredata
migration set once per process, and skips when no database is
reachable so make test stays a pure unit-test run. Migrate the
agentrun, coredata, cookiebanner, iam, and thirdparty suites onto it
and delete the duplicated helpers so the bootstrap can no longer
diverge.
Signed-off-by: Bryan Frimin <bryan@probo.com>
Propagate graceful-suspend signals through detached run contexts and let
only opt-in suspendable tools re-attach cancellation, so AsTool sub-agents
can checkpoint and restore across nested trees while leaf tools keep
running detached.
Add focused agent and worker tests for single and multi-level suspend/
restore flows, plus heartbeat lease-loss and nested-restore error paths to
harden functional behavior under failure conditions.
Signed-off-by: Bryan Frimin <bryan@probo.com>
Move agent-run orchestration from the legacy supervisor path into the new
agentrun worker/service package and wire it through coredata, server,
policies, and GraphQL resolvers.
This consolidates run lifecycle handling around lease-aware workers and
aligns API surface with the new agent-run domain model so reviewers can
follow one coherent execution path.
Signed-off-by: Bryan Frimin <bryan@probo.com>
Auditors could not see the Context page in the console because
AuditorPolicy was missing core:organization-context:get. Grant the
read action (mirroring ViewerPolicy) so the sidebar item appears and
the context resolver succeeds for auditors.
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
Replace the hand-rolled credential chain (static, ECS endpoint, EC2
role) with config.LoadDefaultConfig, which handles the full standard
AWS credential resolution chain automatically.
Co-authored-by: Neil McGibbon <code@neilmcgibbon.com>
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
Remove schema objects that are no longer referenced by any query code,
GraphQL resolver, or frontend and only survive in old migration files:
- users_organizations: superseded by iam_memberships (data was migrated in
20251006T220024Z).
- organizations.logo_object_key: pre-files-table logo storage; replaced by
logo_file_id / horizontal_logo_file_id (data migrated in 20251009T140000Z).
- trust_center_accesses.has_accepted_non_disclosure_agreement,
has_accepted_non_disclosure_agreement_metadata, nda_file_id and
last_token_expires_at: the NDA-acceptance flow now uses
electronic_signature_id together with the state column.
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>