The trust node query previously accepted a String and resolved both GIDs
and slugs through one field, which forced the frontend to lose the ID
type guarantee. Restore node(id: ID!) as a strict GID lookup and add a
dedicated aliasedNode(alias: String!) that parses a GID first and falls
back to slug resolution before delegating to Node.
Inline the former nodeByGID switch directly into Node and drop the helper
file. Point the trust DocumentPage query at aliasedNode so slug-or-ID URLs
keep working.
Signed-off-by: Bryan Frimin <bryan@probo.com>
Rename the resolver helper to ResourceAliasResolver so it reads as a
resolver rather than a storage-resource-specific accessor, and update
its three call sites in the trust center resolvers.
Signed-off-by: Bryan Frimin <bryan@probo.com>
Replace the trust center alias console and MCP end-to-end tests with
resource alias equivalents exercising the new mutations and tools.
Signed-off-by: Bryan Frimin <bryan@probo.com>
Update the compliance page alias field and list items to call the
generic setResourceAlias and removeResourceAlias mutations instead of
the trust-center-specific ones, and drop the now-removed audit alias
field usage.
Signed-off-by: Bryan Frimin <bryan@probo.com>
Move the set and remove alias operations out of the trust center
resource into a dedicated resourceAlias resource so n8n workflows can
alias any resource.
Signed-off-by: Bryan Frimin <bryan@probo.com>
Promote the trust-center alias subcommand to a top-level
resource-alias command so aliases can be managed for any resource
from the CLI.
Signed-off-by: Bryan Frimin <bryan@probo.com>
Rename the setTrustCenterAlias and removeTrustCenterAlias MCP tools to
setResourceAlias and removeResourceAlias, backed by the resourcealias
service.
Signed-off-by: Bryan Frimin <bryan@probo.com>
Switch the trust center API to resolve aliased nodes via the
resourcealias service instead of the trust-center-specific alias
resolvers.
Signed-off-by: Bryan Frimin <bryan@probo.com>
Drop the setTrustCenterAlias and removeTrustCenterAlias mutations and
the alias field on Audit in favor of generic setResourceAlias and
removeResourceAlias mutations backed by the resourcealias service.
Signed-off-by: Bryan Frimin <bryan@probo.com>
Introduce a standalone resourcealias package with its own service,
IAM policies, and OAuth2 scopes so alias management no longer lives
inside the trust center services. Remove the trust-center-specific
alias services from probo and trust, and wire the new service into
probod, the server, and the API layer.
Signed-off-by: Bryan Frimin <bryan@probo.com>
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>
The audit alias resolver returned raw service errors. Log them
and return gqlutils.Internal like other resolvers in the file.
Remove-only users could edit the alias field to a new value that
was never saved. Reset local state when set permission is missing,
and catch mutation rejections on blur.
Sitemap generation appended audit report file IDs without
deduplication, which could emit duplicate document URLs when
multiple audits share the same report file.
Signed-off-by: Bryan Frimin <bryan@probo.com>
Document, file, and audit rows now navigate to /documents/<alias>
when an alias is set, falling back to the resource ID otherwise.
The document page query accepts a string ID to support both forms.
Signed-off-by: Bryan Frimin <bryan@probo.com>
Adds an editable alias column to the documents, files, and audits
tables so operators can set and clear aliases from the console.
Signed-off-by: Bryan Frimin <bryan@probo.com>
OpenAI rejects schemas where optional properties are absent from the
required array. Promote all properties to required and mark formerly
optional ones nullable so the model knows it may pass null.
Also upgrade tool error log level from Warn to Error.
Signed-off-by: Bryan Frimin <bryan@probo.com>
Node lookup now accepts an alias slug in addition to a GID, resolving
it against the organization's alias table before dispatching. Adds
alias fields to Document, AuditReport, and TrustCenterFile.
Signed-off-by: Bryan Frimin <bryan@probo.com>
Adds an alias field to Document, Audit, and TrustCenterFile types.
Introduces setTrustCenterAlias and removeTrustCenterAlias mutations
with proper authorization and error handling.
Signed-off-by: Bryan Frimin <bryan@probo.com>
The sitemap now covers files and audit reports alongside documents
and resolves aliases so human-readable paths appear when configured.
Signed-off-by: Bryan Frimin <bryan@probo.com>
Exposes Create, Remove, GetByResourceID, and LoadByResourceIDs.
Registers ActionTrustCenterAliasSet and ActionTrustCenterAliasRemove
under the trust center write OAuth2 scope.
Signed-off-by: Bryan Frimin <bryan@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>
Tighten redirect URI validation for metadata documents, honor
Cache-Control no-store when caching fetched documents, and resolve
clients on the same transaction as authorization. Load
external_client_id from the database and parse unbounded max-stale
directives in cachecontrol.
Signed-off-by: Bryan Frimin <bryan@probo.com>
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>
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>
The tracker-mapping worker re-arms same-banner siblings after a pattern
resolves a vendor. Its predicate only excluded promoted siblings
(third_party_id IS NULL), but since org-party auto-creation was dropped a
pattern can resolve a common third party yet never gain an org
third_party_id. Those siblings, and terminal first-party ones, stayed
eligible forever, so every cascade step re-enqueued and reprocessed them,
amplifying Process runs to O(N^2) per banner. The deadlock fix in the
last release removed the rollbacks that had accidentally throttled the
cascade, so the latent amplification surfaced as an INFO-log flood.
Tighten the re-enqueue to skip siblings already linked to a catalog row
that carries a common third party or marked FIRST_PARTY, dropping
per-banner reprocessing back to O(N). Also demote the two per-run handler
logs ("mapped tracker pattern", "re-enqueued unmapped sibling tracker
patterns") to Debug so routine processing no longer logs at INFO.
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>
Wrap the usePreloadedQuery calls whose explicit type arguments pushed
them past the 120-character limit, and merge the two duplicate
@probo/helpers imports in FrameworkControlPage into one. These were
flagged by reviewdog on the dependency-upgrade PR.
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>
The linter's project service ignores the generated Relay artifacts,
so the untyped usePreloadedQuery call left viewer as an error type and
triggered no-unsafe-argument/member-access errors. Pass the explicit
query generic like the sibling audit-log page does.
Drop the redundant GraphQLError cast on the revoke error handler since
formatError already accepts the callback's error type.
Signed-off-by: Émile Ré <emile@probo.com>
Bump React 19.2, Relay 21, React Router 8, Vite 8 with
@vitejs/plugin-react 6, ESLint 10, GraphQL 17, TypeScript 6,
@types/node 24, and Tailwind 4.3 across the workspaces.
vite-plugin-react 6 (Vite 8) no longer runs Babel, so the Relay
tagged-template transform now runs through @rolldown/plugin-babel
in the console and trust Vite configs.
Relay 21 ships first-party types and enables the ambiguous-alias
check by default; disable that flag to preserve existing queries
and add explicit usePreloadedQuery type arguments where the new
types no longer infer the operation. TypeScript 6 deprecations and
stricter inference are addressed in tsconfigs and call sites.
Keep n8n-node on ESLint 9 and eslint-plugin-react on 7.37.5, the
newest releases compatible with their toolchains.
Signed-off-by: Émile Ré <emile@probo.com>
Local storage, IndexedDB, and cache storage have no expiry yet
persist until explicitly cleared, so a missing max-age should read
as "persistent", not "session" (the latter only fits cookies and
session storage, which end with the session or tab).
Thread the tracker type through humanizeSeconds (helpers) and
humanizeDuration (cookie-banner, with a localized persistent label)
and pass it at every console and banner call site. The consent
record query now selects trackerType so its duration column can
make the same distinction. This mirrors the Go HumanizedDuration
helper that already renders these types as persistent.
Signed-off-by: Émile Ré <emile@probo.com>
The tracker-mapping worker runs many Process calls in parallel. In
Phase 4 a single transaction locked the worker's own claimed pattern
row via UpdateMapping and then locked sibling rows on the same banner
via the re-enqueue. Two workers mapping sibling patterns on one banner
each held their own row and waited on the other's, forming a lock cycle
that Postgres aborted with deadlock detected (40P01).
Split the sibling re-enqueue into its own short transaction that runs
after the mapping commits, so the claimed-row lock is released before
any sibling row is locked. Also take the sibling UPDATE row locks in a
deterministic id order through an ORDER BY id ... FOR UPDATE subquery,
so overlapping re-enqueues can no longer invert lock order between
themselves. The re-enqueue only flags siblings, so deferring it past
the commit is safe and lets reprocessed siblings observe committed data.
Signed-off-by: Émile Ré <emile@probo.com>
The tracker-pattern source ranking collapsed HTTP into the PRE_EXISTING
tier, so a cookie first enumerated as pre-existing and later re-observed
only via a Set-Cookie response header stayed pre-existing. That left it
on the agent-skipped tier (isPreExistingSource), even though an HTTP
server-set cookie is real page evidence, not the extension-state
catch-all the skip was built to suppress.
Give HTTP its own rank between SCRIPT and EXTENSION
(SCRIPT > HTTP > EXTENSION > PRE_EXISTING) in both sourceRank and the
bestSource merge rollup, so an HTTP re-detection now promotes the
pattern and re-arms mapping, unblocking the identification agent.
Signed-off-by: Émile Ré <emile@probo.com>