Migrate business and security owners into a shared administrators list across GraphQL, MCP, CLI, n8n, and the console.
Signed-off-by: Sacha Al Himdani <sacha@probo.com>
Rename button-styled Link/Anchor to ButtonLink/
ButtonAnchor and add underlined text Link/Anchor
so names match look and element. Hero meta uses
plain Anchors for contact and custom links.
Signed-off-by: Émile Ré <emile@probo.com>
Omiting destructiveHint defaults to true in the MCP spec, so reads
must set it explicitly even though the field is only meaningful when
not read-only.
Signed-off-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
Fill in readonly, destructive, idempotent, and openWorld on every
tool so clients can rely on explicit values instead of MCP defaults
(destructive and openWorld both default to true when omitted).
Drop the temporary mcpgen vendor from this change; that lands in a
separate pull request.
Signed-off-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
Claude and other MCP clients use title, readOnlyHint, and
destructiveHint to present reads, writes, and deletes accurately.
Add a title to every tool, mark missing delete/unlink/cancel/void
tools as destructive, and teach mcpgen to emit those annotations
(including destructiveHint: false for non-destructive writes).
Temporary third_party/mcpgen fork until title support lands
upstream.
Signed-off-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
Signed-off-by: Cursor Agent <cursoragent@cursor.com>
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>
Introduce make lint-shell / fmt-shell with shellcheck and
shfmt (-i 2 -ci -bn), normalize first-party scripts, and
document the new targets. Keep them out of make lint / fmt.
Signed-off-by: Ludovic Vielle <ludovic@probo.com>
The master PNG was fully opaque, so its white corners showed as a
square frame once macOS composited the icon onto its rounded plate.
Swap in the auditor-mode artwork, which has transparent corners.
Signed-off-by: Ludovic Vielle <ludovic@probo.com>
The macOS URL-handler bundle had no CFBundleIconFile, so
Finder showed the generic app icon after PKG install. Keep a
single master PNG and generate AppIcon.icns at build time
with sips/iconutil, matching the auditor-mode approach.
Signed-off-by: Ludovic Vielle <ludovic@probo.com>
Browser enrollment used osascript on every elevate. Ship a signed
privileged helper installed at PKG time so probo:// can enroll over
XPC with no second admin prompt. Add make install/uninstall/clean for
local PKG test loops, and show alerts only on failure.
Mirror the Go lint path for the macOS SPM package: Make
targets, root configs, and a Linux CI job. Keep checks
syntax-only so they do not need a macOS SDK. Format the
existing sources so the new gates start clean.
Signed-off-by: Ludovic Vielle <ludovic@probo.com>
Publish a notarized arm64+x86_64 .pkg from CI with the CGO tray
binary, Probo Agent.app, and global LaunchAgent. Keep the
LaunchDaemon enrollment-gated, align its plist path with the
launchd label, and document the Apple signing secrets.
Signed-off-by: Ludovic Vielle <ludovic@probo.com>
Introduce device, posture, and enrollment-token entities with ITAM
service policies for agent-managed fleet inventory.
Signed-off-by: Ludovic Vielle <ludovic@probo.com>
Compliance portal already embeds and serves the public SPA, so the
old @probo/trust app was unused. Drop its Makefile/CI stubs, Relay
project, CORS port, and the helper that only it imported.
Signed-off-by: Émile Ré <emile@probo.com>
Update generated test config, loopback HTTPS client, and workflow
root CA wiring for the step-ca ACME directory and port-443 trust
center listener introduced by the new compose stack.
Signed-off-by: Bryan Frimin <bryan@probo.com>
Add the complianceportal Go embed target, wire it into CI and
release workflows, and document the new build entry point.
Signed-off-by: Bryan Frimin <bryan@probo.com>
pkg/iam exports IAMOAuth2ScopeMappings, not OAuth2ScopeMappings.
Call out that exception in the agent docs so IAM actions are not
left unregistered for OAuth2 callers.
Signed-off-by: Émile Ré <emile@probo.com>
OAuth2 tokens with v1:compliance-page could not create commitment
groups or items because the IAM actions were never listed in
OAuth2ScopeMappings. Document the mapping step so MCP/API work
does not skip it again.
Signed-off-by: Émile Ré <emile@probo.com>
Introduce a v2 z-1…z-6 stacking scale so portaled
menus sit above in-page media, and redirect the
legacy /overview trust-app URL to home.
Signed-off-by: Émile Ré <emile@probo.com>
Embed and build @probo/compliance-portal for the
/trust path and custom-domain SPA so production
ships the v2 portal. Keep apps/trust in the repo
for local use on port 5175; portal takes 5174.
Signed-off-by: Émile Ré <emile@probo.com>
Guard ErrorBoundary against a falsy thrown value: track a separate
hasError flag and accept the caught value as unknown, so throwing null,
0, or "" still renders the fallback instead of looping back into the
failing subtree.
Add role="alert" to InlineError and ErrorState so assistive tech
announces errors that appear dynamically after a fetch or query failure.
Fix the RecentUpdatesSection doc example, which showed onRetry={reset}
with @throwOnFieldError even though reset cannot clear a field error;
use a reload and point to the retry table.
Signed-off-by: Émile Ré <emile@probo.com>
Contain field errors on the subprocessors and updates lists to an inline
fallback instead of the whole page, and make the retry actually recover.
Introduce ListErrorBoundary, which keeps refetch above the boundary and
resets only after the network refetch settles (a bare boundary reset
re-reads the same errored record and throws again). Wire the subprocessors
and updates lists to refetch network-only on retry, and mark the item
fragments @throwOnFieldError so a row error lands below the boundary.
Fix the home sections, whose reset-only retry could not clear a field
error from the preloaded query, to reload the page instead. Generalize the
retry guidance in the error-handling guide (reset vs refetch vs reload).
Signed-off-by: Émile Ré <emile@probo.com>
React Router routes render and loader errors to the route-level
boundaries, so the App-level boundary above the router could only catch
provider render failures — which today are trivial — while true bootstrap
failures throw at module load before it mounts. Neither console nor trust
wraps App this way.
Rely on the root route boundary instead and remove the BootstrapError
fallback it used.
Signed-off-by: Émile Ré <emile@probo.com>
Introduce global, page, and section-level error handling for the
compliance portal so a failure is contained at the smallest possible
scope instead of blanking the whole page.
Add a portal-local Relay fetch that throws only request-level errors
(and always redirects on UNAUTHENTICATED) while leaving field-level
errors in the response, so Relay surfaces them at the reading component
through @throwOnFieldError and the nearest boundary. Add a NotFoundError
for node __typename mismatches mapped to a not-found page.
Ship reusable v2 kit primitives (ErrorBoundary, ErrorState, InlineError)
matching the Figma global/local/inline designs, wire the bootstrap and
route boundaries, and demonstrate section and row boundaries on the home
page. Update the error-handling and relay guides accordingly.
Signed-off-by: Émile Ré <emile@probo.com>
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>
Ship compliance skills and agent plugin wiring for Claude Code,
Codex, OpenCode, and Cursor via Probo MCP (OAuth 2.0). Includes
access-review and missing-signatures workflows, repo-root Git
marketplaces, validation, and npm release CI.
Signed-off-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
Codify the lessons from the subprocessors filter fix as reusable guidance:
a pure URL-state filter hook (never a per-instance mirror + write-back
effect), a single-owner debounced search hook, and refetching inside a
transition to scope the loading state to the results instead of the
whole-page Suspense fallback.
Add a list-filtering Cursor rule and expand the state-management and relay
guides with the corresponding sections.
Signed-off-by: Émile Ré <emile@probo.com>
probod-bootstrap could only resolve aws:// Secrets Manager refs.
Add awsps:// for SSM Parameter Store (GetParameter with decryption)
and awssm:// as an explicit Secrets Manager prefix. Keep aws:// for
backward compatibility. Values are cached per run per backend.
Update Helm, .env.example, and probod-bootstrap changelog.
Signed-off-by: Ludovic Vielle <ludovic@probo.com>
Fix the valid issues raised in the scaffold review.
UI kit: the Button loading state now replaces only the leading icon
instead of dropping the label, Button consumes the `active` variant so
it no longer leaks onto the DOM, and every v2 skeleton sets aria-hidden
after the prop spread so a consumer cannot override it.
@probo/relay: guard the caller-supplied onCompleted/onError callbacks so
a throwing callback still settles the awaitable mutation promise instead
of leaving it pending.
compliance-portal: normalize external website hrefs and read hostname
via URL.hostname, add a localized catch-all not-found route, and widen
the .gitattributes glob so colocated __generated__ artifacts at any depth
are marked generated.
Docs: correct the forms guide (Base UI passes plain values, Zod v3
flatten API), spread the child fragment in the permissions example, and
drop references to v2 components that do not exist in the ui guide.
Signed-off-by: Émile Ré <emile@probo.com>
Capture two conventions surfaced while building the top bar: use the
Relay @required directive to make expected-present nullable fields
non-null for consistent typing, and reserve default exports for the
component that lazy() imports as a bundle entry while everything else
uses named exports.
Signed-off-by: Émile Ré <emile@probo.com>
The numbered v2 token scales (text-1…9, rounded-1…6, shadow-1…6)
share a prefix with the color/utility namespaces, and tailwind-merge
(which tailwind-variants runs by default) cannot tell a numeric
text-3 from a color step, so it silently dropped the size whenever a
text-<color> was present. Switch the v2 kit to the merge-free
tailwind-variants/lite entrypoint (added in v3): upgrade
tailwind-variants to ^3.2.2, import tv/VariantProps from
tailwind-variants/lite in the typography primitives, and drop the
per-call twMerge:false.
The legacy v1 kit stays on tailwind-variants (merge intact). Document
the rationale in ui.md and add a packages/ui/src/v2 ESLint guard that
forbids tailwind-variants, tailwind-merge, and clsx.
Signed-off-by: Émile Ré <emile@probo.com>
Apply small follow-up edits to the frontend documentation: the
AGENTS index, the forms, react-components, and ui guides.
Signed-off-by: Émile Ré <emile@probo.com>
Extract the awaitable useMutation into @probo/relay as a
createUseMutation factory that delegates feedback to an injected
MutationNotifier, keeping the package free of UI and i18n
dependencies. compliance-portal binds it to its Base UI toast +
i18next + formatError stack and imports it by explicit path
(#/lib/relay/useMutation), dropping the lone intra-app barrel; a
compliance-portal-scoped no-restricted-imports rule forbids
react-relay's useMutation.
Bring packages/relay and packages/routes into the shared ESLint
scope and fix the violations that surfaced, and deprecate the
legacy withQueryRef / loaderFromQueryLoader helpers. Document the
shared-hook pattern and the "index.ts for package entrypoints only"
rule in the relay, hooks, and app-arborescence guides.
Signed-off-by: Émile Ré <emile@probo.com>
Relay 21 made the filename-prefix requirement opt-in for non-Haste
projects, and the relay/graphql-naming lint rule only covers
operations and legacy fragment containers, leaving hooks-based
fragment names unguarded. Re-enable compiler enforcement via
enforce_module_name_prefix_for_non_haste so fragments keep the
collision-free, navigable <ModuleName>_<localName> convention, and
document the rationale in the Relay guide. The compiler accepts the
current sources unchanged, so existing names already comply.
Signed-off-by: Émile Ré <emile@probo.com>
The v2 token guide endorsed raw <h1>/<p> with hand-applied step
classes, which invites token drift across app code. Add a UI guide
section establishing that content text flows through kit typography
components (Text, Heading, Code) and that raw elements are reserved
for kit internals and structural, non-textual nodes. Cross-link it
from the typography token section so the two guides stay aligned.
Signed-off-by: Émile Ré <emile@probo.com>
Add the frontend guides the v2 UI kit and compliance-portal need but
that the first rework left uncovered: forms, routing, client state, and
permission-gated UI.
forms.md documents a tiered approach on Base UI Field/Form -- native
constraints, then a validate function, then zod parsed in onSubmit, and
react-hook-form only for large or dynamic forms -- and drops the custom
useFormWithSchema wrapper. routing.md covers @probo/routes, navigation,
typed params, URL-as-state, redirects, auth/protected routes, and the
folded-in no-outlet-context rule. state-management.md gives a decision
order across Relay, URL, local state, context, and zustand.
permissions.md gates UI on the canUpdate/canDelete permission(action:)
fields without re-encoding authorization in the client.
Rename v2-colors.md to v2-tokens.md and add the typography, radius,
shadow, and native-spacing scales alongside color. Extend ui.md with
user feedback, empty-state, and accessibility sections; standardize
toasts on Base UI's Toast (Toast.useToastManager) and retire the legacy
useToast across ui.md, forms.md, error-handling.md, and relay.md. Add an
Intl formatting section to i18n.md and a non-Relay HTTP / file
upload-download section to ts-style.md. Update the AGENTS.md index and
the v2-color-scale cursor rule for the new and renamed guides.
Signed-off-by: Émile Ré <emile@probo.com>
Make contrib/claude the single source of truth for v2 frontend work
on the compliance-portal app and packages/ui/src/v2, treating console
and the legacy @probo/ui tree as non-compliant code to migrate rather
than precedent.
Rewrite ui.md around the v2 kit: flat folders, Base UI as the headless
layer styled thinly (controlled open/onOpenChange, no imperative ref or
cloneElement plumbing), tailwind-variants only, separate components over
structure-changing variants, and bundle-safe skeletons that never drag
Base UI into the loading path.
Add a naming/suffix taxonomy to react-components.md, replacing the
Table/Row and connection-item Card suffixes with List/ListItem, and add
an error/fallback props convention. Document _lib and _locales special
folders plus routes.ts placement in app-arborescence.md, with at most
one _locales per routes.ts.
Add error-handling.md (reusable ErrorBoundary usable at any level plus
async try/catch) and i18n.md (i18next key-based catalogs). Update the
relay file-organization and fragment examples, the connection-item
cursor rule, and the AGENTS.md index to match.
Signed-off-by: Émile Ré <emile@probo.com>
Introduce a Resolver that owns env lookup and typed parsing for
probod-bootstrap. Env values prefixed with aws://<secret-id> are
fetched from AWS Secrets Manager (plaintext SecretString); each
secret ID is cached per run. Builder now takes a Resolver only.
Prefix every probod-bootstrap input with PROBOD_ so bootstrap config
does not collide with unrelated process environment (for example
AWS_* used by other tooling). Secrets Manager authentication uses
the standard AWS SDK default chain (AWS_REGION, IAM role, profile);
PROBOD_AWS_* vars configure S3 in the generated config only.
Update Helm deployment env names, GNUmakefile dev-config, Lima
provision, e2e testutil, compose.prod.yaml, and docs.
Deployments must rename bootstrap env vars to PROBOD_* (e.g.
AUTH_COOKIE_SECRET → PROBOD_AUTH_COOKIE_SECRET).
BREAKING CHANGE: all env vars are now prefixed by `PROBOD_`.
Signed-off-by: Ludovic Vielle <ludovic@probo.com>
Replace pkg/iam/scopeset with pkg/iam/oauth2scope.Registry, a shared
OAuth2 scope→action registry used by the authorizer, OAuth2 service,
and Connect API. Registration stays open until probod calls Freeze();
read paths (RegisteredScopes, Allows, ValidateScopes) panic before
that.
Drop the leaky APIScopes surface and AllowedAPIScopes on manual
access-token creation in favor of registry.ValidateScopes. Metadata,
protected-resource metadata, and CIMD scope lists are built from
RegisteredScopes() via helpers in pkg/iam/oauth2/scopes.go. Expose
oauth2ScopesSupported as an OAuth2Scope GraphQL scalar.
Signed-off-by: Ludovic Vielle <ludovic@probo.com>
Replace the duplicated per-workspace eslint.config.mjs files in
apps/console, apps/trust, packages/ui, and packages/eslint-config with
one root eslint.config.mjs that reuses the shared @probo/eslint-config
rule sets and scopes them per directory. Linting now runs from the repo
root, so pin the type-checked project service root and broaden the
import-x resolver to every workspace tsconfig (the #/* aliases live in
each app's tsconfig.app.json).
Drop the now-redundant per-package lint scripts and lint-only devDeps,
and add a root lint script that runs eslint over the four dirs with
multithreading restored via --concurrency auto, then lints n8n-node
through a direct workspace call. packages/n8n-node keeps its own
external preset.
Collapse the redundant lint-js -> npm-lint Makefile chain into a single
lint-js target and update the make docs accordingly.
Signed-off-by: Émile Ré <emile@probo.com>
relay-compiler requires a single schema file: a directory is rejected
and schemaExtensions marks fields as client-only, so it emitted
text: null and the console posted query: null, getting a 400 on every
operation.
Restore the merge step (contrib/merge-graphql-schema.sh, the
RELAY_SCHEMAS make rules, and the gitignore entry) and point each
relay.config.json project back at the merged schema.graphql. The IDE
graphql-config removal and npm-script cleanup are unrelated and stay.
Signed-off-by: Émile Ré <emile@probo.com>
Relay no longer needs a single merged schema.graphql: each project in
relay.config.json now reads the split graphql/*.graphql files directly
via `schema` (base.graphql) plus `schemaExtensions`. gqlgen already
consumed the split files, so the merge step only fed Relay and the
optional IDE GraphQL extension.
Remove the merge machinery (contrib/merge-graphql-schema.sh, the
RELAY_SCHEMAS make rules, and the gitignore entry) and drop the
graphql-config files (apps/trust/graphql.config.yml and the root
package.json graphql field); the Relay extension provides schema-aware
language features from relay.config.json on its own.
relay-compiler keeps generated artifacts in sync (stale ones are
removed automatically), so the relay npm script just runs the local
relay-compiler and the make target delegates to it.
Signed-off-by: Émile Ré <emile@probo.com>
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>
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>
Register v1 API scopes in coredata, advertise them in OIDC discovery
and protected-resource metadata, show them on the consent screen, and
enforce scope-to-action mapping in the IAM Authorizer before policy
evaluation.
Signed-off-by: Ludovic Vielle <ludovic@probo.com>
Radix documents the 11/12 APCA guarantee against a step 2 background
only, not step 1. Correct the claim and the example (which used
bg-sand-1), add the precise Lc 60 / Lc 90 targets and a source link,
and note that step 1 is safe in practice but not part of the stated
guarantee.
Signed-off-by: Émile Ré <emile@probo.com>