Skip to content

feat: Guardian 3.7.1 - #7092

Merged
Pyatakov merged 958 commits into
mainfrom
develop
Oct 6, 2026
Merged

Pyatakov merged 958 commits into
mainfrom
develop

Conversation

@Pyatakov

@Pyatakov Pyatakov commented Oct 5, 2026

Copy link
Copy Markdown
Contributor

No description provided.

vshvets-bc and others added 30 commits August 20, 2026 19:47
The administered organization (an active member whose role carries MEMBER_MANAGE) was appended to the result after the query, and only when offset was 0. Three consequences:
- the reported total changed between pages - 1 on page 0, 0 on page 1 for a delegated admin who owns nothing - and page 0 carried pageSize + 1 items, so a paging client cannot reconcile the two;
- the dedup checked the current page's items only, so an owner who is also enrolled with MEMBER_MANAGE and owns more than pageSize organizations received the row twice, with the count double-counting it;
- the name filter was a regex on the owned query and String.includes on the appended row, so the two branches disagreed on case sensitivity.

Fold the administered organization into the query as an $or term instead. The count and the paging then come straight from findAndCount, and a document cannot match a query twice, so the dedup is inherent.

The name filter is also escaped now: it is user input interpolated into a $regex, so `?name=[` reached Mongo as an invalid expression and produced a 500 rather than a result set. It is applied case-insensitively, as the API documents.

Redaction is preserved and made owner-based rather than branch-based: a row the caller does not own is returned without its walletToken, whichever term of the $or matched it.
---------
Signed-off-by: Volodymyr Shvets <volodymyr.shvets@climission.com>
…e they belong

* fix(frontend): put misplaced overlays, tooltips and empty states where they belong
Five layout defects, all of the same kind.
- Manage Schemas toolbar – pTooltip is applied to the <p-button> component wrapper, so PrimeNG computes the position from the host's bounding rect rather than the rendered button and the bubble lands away from the control, over the grid. Each tooltip now sits on an inline-flex wrapper and carries tooltipStyleClass="guardian-tooltip", matching the grid-row tooltips that render correctly.
- interfaceDocumentsSourceBlock field config – the document path cell clips its content and the span carries no tooltip, so a value like document.credentialSubject.0.projectId cannot be read at all. It now uses the same tooltip pattern as the other labels in that component.
- Manage Policies empty state – .not-exist is declared twice. The first rule positions it absolutely at 50%/50% of the grid container, taking it out of flow and letting it render on top of the toolbar action buttons; the second already centres it with flex and supersedes every other declaration the first made. The obsolete block is removed so the empty state flows below the toolbar.
- Relayer Accounts search - the icon is placed with a magic `top: 12px` against a wrapper that has no layout of its own, so it detaches from the input whenever the rendered heights differ from the assumed 40px. It is now centred on the input, the wrapper is an explicit flex box so it is unambiguously the offset parent, and the input reserves room for it.
- Add Credential dialog – without appendTo, the modal mask is created next to the dialog inside the page layout, so it covers only that container and leaves an un-dimmed band across the bottom of the page. The dialog is appended to body, matching the other p-dialogs, and picks up the same draggable/resizable/styleClass flags they use.
---------
Co-authored-by: Volodymyr Shvets <volodymyr.shvets@climission.com>
* fix(frontend): remove two controls the user cannot use
Schema Templates in the nav is gated on SCHEMAS_SCHEMA_READ || SCHEMAS_SYSTEM_SCHEMA_READ, but /schema-templates requires TEMPLATES_TEMPLATE_READ. A custom role holding only schema-read sees the item and is bounced to "Access Restricted" by PermissionsGuard - a dead link. It is now gated on the permission the route actually requires.
TEMPLATES_TEMPLATE_READ also has to open the Manage section: without that, gating the item on it would hide it from exactly the roles that can use it.
In the Export Schema dialog, "Copy message identifier" is bound to [disabled]="!schema.messageId". A draft is not published and so has no Hedera message identifier, so on every draft the button renders permanently greyed out with no explanation. It is hidden instead, and shown only for a schema that has one.
That dialog also had no reachable way out: onClose() exists but was never wired to the template, so it closed only programmatically after a successful save. A Cancel button now calls it.
---------
Co-authored-by: Volodymyr Shvets <volodymyr.shvets@climission.com>
* fix(frontend): name the wizard's role steps for what they configure
onSchemaRoleConfigChange and onSelectedTrustChainRoleChange both named their node "<role> configuration", so "OWNER configuration" appeared once under Policy Schemas and again under Trust Chain with nothing to tell them apart.
The navigation is not a loop: SeparateStepperComponent walks the tree depth-first and getNextNode returns nothing at the end, so Next terminates. But two identically named stops read as one the user keeps returning to, which is why the flow feels endless. Naming them for what they configure - "OWNER in Project Description" and "OWNER trust chain" - is enough; the walk is left alone.
A schema role node falls back to the old label if its parent carries no schema, so nothing breaks if the node is built outside the normal path.
---------
Co-authored-by: Volodymyr Shvets <volodymyr.shvets@climission.com>
* fix(frontend): give Schema Templates the shared paginator
The grid enables p-table's built-in pager ([paginator]="true"), which renders only the page arrows - there is no items-per-page control, so the page size cannot be changed. Every other grid in the app uses the shared app-paginator with a rows-per-page selector.
It now uses app-paginator with the standard 10/25/50/100/500 options. onPage reads {pageIndex, pageSize} instead of PrimeNG's {first, rows}, and resets to the first page when the size changes, matching the Schemas grid. The default page size moves from 20 to 25 so it is one of the offered options - otherwise the paginator appends 20 to its own list.
---------
Co-authored-by: Volodymyr Shvets <volodymyr.shvets@climission.com>
* fix(common,frontend): stop dropping schema import errors
Two halves of "make schema import errors and warnings visible" do not reach the user.
common: updateSchemas() pushes an "Unknown field type" error for an unresolvable ref and then falls through to `link.worksheet`, throwing a TypeError that the surrounding catch converts into a single generic "Failed to update schemas." So one bad cell discards every precise error already collected - the opposite of the intent. The missing `continue` keeps them. #SentinelHUB is also skipped alongside #GeoJSON: the schema import pre-seeds both as validated ref types, so neither has a link entry and a SentinelHUB field was reported as an unknown type, which then triggered the crash above.

frontend: the import builds ImportSchemaResult.errors and returns it, but the IMPORT_SCHEMA_FILE / IMPORT_SCHEMA_MESSAGE resolver navigates away without reading it. A bundle that fails def validation completes silently - the only residual signal is the per-schema ERROR badge on the list. The errors now surface as one sticky toast naming each schema that failed.

* review: clear the dangling ref on an unknown field type
Matches what the sibling sub-schema-not-found branch already does; without it the field keeps its unresolved type and survives into the imported schema.
---------
Co-authored-by: Volodymyr Shvets <volodymyr.shvets@climission.com>
* fix(frontend): say which operation started, and show a failed search once
The backend creates every async task's progress notification with the same fixed message ('Operation started' in notification-events.ts), so the toast reads like a generic success and never says what actually began - the action name is only the small title. It now names the operation and phrases it as in-progress. A message that is not the constant passes through untouched, and the toast stays info-styled: it is progress, not success.

Separately, the Search Policy dialog renders preview/import failures inline via `this.error`, and the global HandleErrorsService interceptor toasts the identical message on top of it - so one failure appears twice. Both calls now carry the existing SILENT_HTTP_ERRORS context flag, the same mechanism branding and auth already use. Every other external-policy call keeps the global toast.

* review: never render an empty error box
The dialog reads only e.error.message and the template renders step 0 as a bare {{error}}. With the interceptor toast now suppressed, a response without a JSON message left the user with an empty box.
---------
Co-authored-by: Volodymyr Shvets <volodymyr.shvets@climission.com>
* fix(frontend): guard lists that are read before they load
Three places read a list that may not exist yet, and one of them stays
broken afterwards.
compare-policy, compare-schema and compare-module assign `X?.report` and then immediately walk `X.length`. The `?.` already anticipates an absent report node, but the loop does not, so a degenerate or empty diff throws "Cannot read properties of undefined" after the parent has cleared its spinner. The newer siblings (compare-tool, compare-document, compare-record) harden this exact spot with Array.isArray; the same guard is applied here.

wipe-requests-dialog declares `requests` with no initializer while its template reads `requests.length` on the first render, so opening the dialog throws in change detection. Every sibling dialog (retire-requests, retire-pools, user-retire-pools, user-retire-requests) initialises to []. The load error arm also never assigned `requests`, so a failing
getWipeRequests left the body permanently broken - neither the list nor the empty state could render.

Separately, all eleven load error callbacks in compare.component did only `loading = false; console.error(...)` and never set `error`, so the template's `@if (error)` banner was reachable only from the local "Invalid params" pre-check. A timeout, a 500 or an oversized diff left a blank page with no explanation. They now route through one helper that fills the banner.

* review: give the export failures their own message
The five *File() calls are exports, not loads, so routing them through onLoadFailed told the user the comparison could not be loaded when the comparison was already on screen.
---------
Co-authored-by: Volodymyr Shvets <volodymyr.shvets@climission.com>
* fix: an org policy assignment must belong to the policy's owner
ASSIGN_POLICY_TO_ORG checked that the caller owned the organization and that policyId was non-empty, and nothing else. auth-service owns the assignment row but not the Policy collection, so its handler could not check the policy at all, and no layer above it did either - the gateway route called Users.assignPolicyToOrg straight through to NATS. A Standard Registry could therefore assign any policy id, including another SR's, to their own organization.
That mattered because policy-service's relayed block-action gate honours the assignment: accessPolicy fell back to isPolicyAssignedToUserOrg with no policy-owner comparison, so the assignee's organization members then passed the gate for a policy belonging to someone else.
The gateway is the one layer that can reach both services, so it now resolves the policy and rejects anything the caller does not own - 404 when the policy does not exist, 403 when it belongs to another owner. InternalException re-throws HttpException unchanged, so both statuses survive to the client.

The second half closes the asymmetry itself. guardian-service's accessPolicyCode rejects `user.owner !== policy.owner` before it consults an assignment; the policy-service gate now does the same, so a row written before the gateway check existed is no longer honoured. PolicyUser did not carry the user's owning registry, so it gains `parent` - derived exactly as EntityOwner derives `owner`. Only a known mismatch denies: a virtual (dry-run) user or one built from a bare DID has no parent, and treating unknown as mismatched would break dry-run runs
that never had an owner to compare.
---------
Signed-off-by: Volodymyr Shvets <volodymyr.shvets@climission.com>
* fix: refuse to strand published schemas, free the old package
Deleting a PUBLISH_ERROR template stranded published schemas. ensureEditable blocks only PUBLISHED, and the cleanup removes only DRAFT and ERROR rows - so schemas an earlier publish attempt had already flipped to PUBLISHED survived the template and were left pointing at a templateId that no longer existed. Deletion is now refused while they exist, naming them, mirroring the used-by-policies guard directly above it: nothing that reached a topic is destroyed silently, and the owner is told what to resolve. A template whose schemas are all DRAFT or ERROR still deletes exactly as before.
Each publish overwrote contentFileId without deleting the file it replaced. configFileId has the _configFileId "previous handle" mechanism on the entity for exactly this, cleaned up by its @AfterUpdate hook; contentFileId had none, so a re-publish left a permanent GridFS orphan every time. The old file is removed after the new one is safely stored, and best-effort - losing it is not a reason to fail a publish.
---------
Signed-off-by: Volodymyr Shvets <volodymyr.shvets@climission.com>
* fix(guardian-service): clone a template-bound policy whole, or not at all
resolveSchemaTemplate only tried metadata.schemaTemplate.templateId and a message id, and clonePolicy passes no metadata - so cloning a policy bound to an unpublished template threw "Selected schema template is inaccessible" even though the template was sitting right there in the database. It now falls back to the binding's own templateId, under the same accessibility rule as the metadata branch: published, or owned by the caller.

The other half is the half-bound clone. saveSchemaTemplateSnapshot nulls policy.schemaTemplate when there is no snapshot to remap - exactly what a clone produces - but it runs after the schemas are persisted, so they kept their templateId, templateSchemaId and templateFieldId markers: a policy claiming no template over schemas still claiming one. The decision now happens up front, beside the explicit detach and before the schemas are written, which is what makes the strip persist. A binding with no snapshot to carry is dropped whole.

* review: attribute the binding fallback to the path it actually serves
The comment credited clonePolicy, but a clone carries no snapshot, so mustDropSchemaTemplateBinding nulls the binding before resolveSchemaTemplate runs and the branch is unreachable there - the clone fix is the drop. The branch does cover an import that keeps its snapshot and binds an unpublished but owned template, which otherwise hits the inaccessible throw.
---------
Signed-off-by: Volodymyr Shvets <volodymyr.shvets@climission.com>
Bumps [@meeco/cryppo](https://github.com/Meeco/cryppo-js) from 3.0.1 to 3.0.2.
- [Release notes](https://github.com/Meeco/cryppo-js/releases)
- [Changelog](https://github.com/Meeco/cryppo-js/blob/master/CHANGELOG.md)
- [Commits](Meeco/cryppo-js@releases/3.0.1...releases/3.0.2)

---
updated-dependencies:
- dependency-name: "@meeco/cryppo"
  dependency-version: 3.0.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [@scalar/nestjs-api-reference](https://github.com/scalar/scalar/tree/HEAD/integrations/nestjs) from 1.2.14 to 1.2.16.
- [Release notes](https://github.com/scalar/scalar/releases)
- [Changelog](https://github.com/scalar/scalar/blob/main/integrations/nestjs/CHANGELOG.md)
- [Commits](https://github.com/scalar/scalar/commits/HEAD/integrations/nestjs)

---
updated-dependencies:
- dependency-name: "@scalar/nestjs-api-reference"
  dependency-version: 1.2.16
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [@azure/identity](https://github.com/Azure/azure-sdk-for-js/tree/HEAD/sdk/identity/identity) from 4.13.1 to 4.13.2.
- [Release notes](https://github.com/Azure/azure-sdk-for-js/releases)
- [Changelog](https://github.com/Azure/azure-sdk-for-js/blob/@azure/identity_4.13.2/sdk/identity/identity/CHANGELOG.md)
- [Commits](https://github.com/Azure/azure-sdk-for-js/commits/@azure/identity_4.13.2/sdk/identity/identity)

---
updated-dependencies:
- dependency-name: "@azure/identity"
  dependency-version: 4.13.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps the nestjs group with 6 updates:

| Package | From | To |
| --- | --- | --- |
| [@nestjs/common](https://github.com/nestjs/nest/tree/HEAD/packages/common) | `11.2.0` | `11.2.1` |
| [@nestjs/core](https://github.com/nestjs/nest/tree/HEAD/packages/core) | `11.2.0` | `11.2.1` |
| [@nestjs/microservices](https://github.com/nestjs/nest/tree/HEAD/packages/microservices) | `11.2.0` | `11.2.1` |
| [@nestjs/platform-express](https://github.com/nestjs/nest/tree/HEAD/packages/platform-express) | `11.2.0` | `11.2.1` |
| [@nestjs/swagger](https://github.com/nestjs/swagger) | `11.4.6` | `11.4.7` |
| [@nestjs/platform-fastify](https://github.com/nestjs/nest/tree/HEAD/packages/platform-fastify) | `11.2.0` | `11.2.1` |


Updates `@nestjs/common` from 11.2.0 to 11.2.1
- [Release notes](https://github.com/nestjs/nest/releases)
- [Commits](https://github.com/nestjs/nest/commits/v11.2.1/packages/common)

Updates `@nestjs/core` from 11.2.0 to 11.2.1
- [Release notes](https://github.com/nestjs/nest/releases)
- [Commits](https://github.com/nestjs/nest/commits/v11.2.1/packages/core)

Updates `@nestjs/microservices` from 11.2.0 to 11.2.1
- [Release notes](https://github.com/nestjs/nest/releases)
- [Commits](https://github.com/nestjs/nest/commits/v11.2.1/packages/microservices)

Updates `@nestjs/platform-express` from 11.2.0 to 11.2.1
- [Release notes](https://github.com/nestjs/nest/releases)
- [Commits](https://github.com/nestjs/nest/commits/v11.2.1/packages/platform-express)

Updates `@nestjs/swagger` from 11.4.6 to 11.4.7
- [Release notes](https://github.com/nestjs/swagger/releases)
- [Commits](nestjs/swagger@11.4.6...11.4.7)

Updates `@nestjs/platform-fastify` from 11.2.0 to 11.2.1
- [Release notes](https://github.com/nestjs/nest/releases)
- [Commits](https://github.com/nestjs/nest/commits/v11.2.1/packages/platform-fastify)

---
updated-dependencies:
- dependency-name: "@nestjs/common"
  dependency-version: 11.2.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: nestjs
- dependency-name: "@nestjs/core"
  dependency-version: 11.2.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: nestjs
- dependency-name: "@nestjs/microservices"
  dependency-version: 11.2.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: nestjs
- dependency-name: "@nestjs/platform-express"
  dependency-version: 11.2.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: nestjs
- dependency-name: "@nestjs/swagger"
  dependency-version: 11.4.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: nestjs
- dependency-name: "@nestjs/platform-fastify"
  dependency-version: 11.2.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: nestjs
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [tsc-alias](https://github.com/justkey007/tsc-alias) from 1.9.1 to 1.9.2.
- [Release notes](https://github.com/justkey007/tsc-alias/releases)
- [Commits](justkey007/tsc-alias@v1.9.1...v1.9.2)

---
updated-dependencies:
- dependency-name: tsc-alias
  dependency-version: 1.9.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps the langchain group with 4 updates: [@langchain/classic](https://github.com/langchain-ai/langchainjs), [@langchain/core](https://github.com/langchain-ai/langchainjs), [@langchain/openai](https://github.com/langchain-ai/langchainjs) and [langchain](https://github.com/langchain-ai/langchainjs).


Updates `@langchain/classic` from 1.0.42 to 1.0.45
- [Release notes](https://github.com/langchain-ai/langchainjs/releases)
- [Commits](https://github.com/langchain-ai/langchainjs/compare/@langchain/classic@1.0.42...@langchain/classic@1.0.45)

Updates `@langchain/core` from 1.2.7 to 1.2.9
- [Release notes](https://github.com/langchain-ai/langchainjs/releases)
- [Commits](https://github.com/langchain-ai/langchainjs/compare/@langchain/core@1.2.7...@langchain/core@1.2.9)

Updates `@langchain/openai` from 1.5.7 to 1.5.10
- [Release notes](https://github.com/langchain-ai/langchainjs/releases)
- [Commits](https://github.com/langchain-ai/langchainjs/compare/@langchain/openai@1.5.7...@langchain/openai@1.5.10)

Updates `langchain` from 1.5.8 to 1.5.10
- [Release notes](https://github.com/langchain-ai/langchainjs/releases)
- [Commits](https://github.com/langchain-ai/langchainjs/compare/langchain@1.5.8...langchain@1.5.10)

---
updated-dependencies:
- dependency-name: "@langchain/classic"
  dependency-version: 1.0.45
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: langchain
- dependency-name: "@langchain/core"
  dependency-version: 1.2.9
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: langchain
- dependency-name: "@langchain/openai"
  dependency-version: 1.5.10
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: langchain
- dependency-name: langchain
  dependency-version: 1.5.10
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: langchain
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [@aws-sdk/client-secrets-manager](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/clients/client-secrets-manager) from 3.1110.0 to 3.1115.0.
- [Release notes](https://github.com/aws/aws-sdk-js-v3/releases)
- [Changelog](https://github.com/aws/aws-sdk-js-v3/blob/main/clients/client-secrets-manager/CHANGELOG.md)
- [Commits](https://github.com/aws/aws-sdk-js-v3/commits/v3.1115.0/clients/client-secrets-manager)

---
updated-dependencies:
- dependency-name: "@aws-sdk/client-secrets-manager"
  dependency-version: 3.1115.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [js-base64](https://github.com/dankogai/js-base64) from 3.9.2 to 3.9.3.
- [Commits](dankogai/js-base64@3.9.2...v3.9.3)

---
updated-dependencies:
- dependency-name: js-base64
  dependency-version: 3.9.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* typo: MIT should say —> Apache 2.0

Signed-off-by: Daniel Swid <daniel.swid@hashgraph.com>
The validity check in startCron is inverted: when the parsed start date is valid it is thrown away and replaced with the current time, so the configured startDate never reaches the cron mask. Every yearly, monthly, weekly, daily and hourly timer therefore anchors to whenever the policy last started, and shifts again after each restart.

An unparseable start date takes the opposite path and stays invalid, so minute()/hour() yield NaN, the mask becomes "NaN * * * *" and CronJob rejects it, surfacing as "start scheduler fail".

Fall back to the current time only when the start date cannot be parsed.

Co-authored-by: Volodymyr Shvets <volodymyr.shvets@climission.com>
…cy hash

* fix: keep required custom fields, strip $defs markers, stabilize policy hash
1. A preserved custom field lost its required flag.
preparePolicySchemaUpdate replaces the target document with a fresh clone of the template, so its `required` list becomes the template's, then asks mergeCustomFieldsIntoDocument to put the caller's own fields back. That re-inserted them into `properties` only - and `required` lives on the parent, not on the property - so a required custom field survived a template update as optional, and VCs missing it started validating. The flag is now carried across with the field, onto the field's own parent rather than the root.
2. Detach left templateFieldId markers inside $defs.
SchemaHelper.removeTemplateFieldIds walks via walkDocumentProperties, which only follows `properties`, so embedded sub-schema definitions kept their markers after a detach - inconsistent with the cleaned sub-schema rows, and leaking into subsequently published documents. Only removal walks $defs; ensureTemplateFieldIds is deliberately left alone so this cannot start minting ids in places that never had them.
3. The template binding destabilized the policy hash.
cleanBeforeHash already dropped components.schemaTemplateSnapshot, but policy.schemaTemplate carries a snapshotId, schemaMap ObjectIds and appliedAt/updatedAt, and each schema carries templateId / templateSchemaId - all assigned per environment. Exporting and re-importing an identical template-bound policy therefore produced a different hash every time, so hash-based same-policy detection reported a difference for every template-bound policy.

mergeCustomFieldsIntoDocument is exported for its test, matching how normalizeFieldForDiff and buildFieldChangeDetails are already exported from that module.

* review: collapse the duplicate walker, keep reading required from the document
findSchemaPropertyParent and ensureSchemaPropertyParent walked the same path and differed only in whether they created the `properties` containers on the way down. They are now one helper with a `create` flag - ensureSchemaPropertyParent stays as a thin alias for the existing caller.
---------
Signed-off-by: Volodymyr Shvets <volodymyr.shvets@climission.com>
A published policy that has a restore topic runs PolicyBackupService, whose debounce timer is armed by block activity. timerBlock arms it on every cron tick, so a policy with a short timer period produces a diff every tick even when no collection changed.

Each of those diffs costs an IPFS upload and a Hedera TopicMessageSubmitTransaction to record that nothing happened, and rewrites the stored backup file for no reason.

Skip publishing and storing when an incremental diff carries no actions in any collection. Full backups and key documents are never skipped, and the restore side does not require contiguous diff indexes.

Co-authored-by: Volodymyr Shvets <volodymyr.shvets@climission.com>
PolicyParametersDialog and VCFullscreenDialog each guard subscriptions with takeUntil(this._destroy$) but neither implemented OnDestroy and neither ever called _destroy$.next(). The guard read as cleanup and did nothing, so every open-and-close leaked - eight subscriptions between them, on dialogs that are opened repeatedly.

VCFullscreenDialog could not simply complete _destroy$: a caller may hand in its own subject as a close-the-dialog signal, and completing it would tear down something the parent still owns. It gets a private _viewDestroyed$ for the seven takeUntil sites; _destroy$ stays the external contract and its subscription is unsubscribed rather than completed.

Co-authored-by: Volodymyr Shvets <volodymyr.shvets@climission.com>
* fix(guardian-service): stop schema-template reads writing, scope usedByPolicyNames
1. Read paths persisted normalization ids.
normalizeSchemaTemplateConfig / ensureTemplateSchemaReferences assign any missing templateSchemaId and templateFieldId and then wrote them back with DatabaseServer.updateSchema. Those helpers run from GET_SCHEMA_TEMPLATE, SCHEMA_TEMPLATE_EXPORT_FILE and the update *preview* as well as from the write paths. A published template is readable by every user holding TEMPLATES_TEMPLATE_READ, so a non-owner's GET mutated the owner's schema documents, and two concurrent readers raced to store different random ids for the same field. Both helpers now take a `persist` flag, default true; the three read paths pass false. The ids are still filled in memory, so responses are byte-for-byte unchanged - they are only written when the caller is performing a write.
2. usedByPolicyNames was returned to every reader.
The names come from the *template owner's* policies, drafts included, while the listing shows published templates to everyone with TEMPLATES_TEMPLATE_READ. That disclosed another Standard Registry's private draft-policy names. addSchemaCounts now takes the caller and returns the names only to the template owner. usedByPoliciesCount is unchanged for everyone: it says how widely a template is used without naming anything.

* review: make the safe behaviour the default
addSchemaCounts takes a required owner, so omitting it is a type error rather than silent data loss. persist defaults to false on all three helpers and the six write call sites pass true explicitly. The import path seeds templateSchemaId and the template field ids the way importSchemaTemplateByComponents already does, so new rows carry them from the start.
---------
Signed-off-by: Volodymyr Shvets <volodymyr.shvets@climission.com>
* fix(frontend): scope the app theme to the signed-in user
The theme was stored under one global key, GUARDIAN_APP_THEME. A browser is shared, so the preference one account chose was applied to the next account that signed in on the same machine, and survived signing out entirely.

The key is now suffixed with the user id. AuthStateService adopts the signing-in user's preference and drops back to the default when the session ends, so the theme follows the account rather than the machine. The pre-upgrade global key is still read as a fallback when an account has no stored choice of its own, so an existing preference is not silently lost on upgrade.

The bootstrap call in the AuthStateService constructor - updateState(false, true) - is not a sign-out and must not reset; doing so would discard the theme on every page refresh. That is what the noClearLocalStorage flag distinguishes.

AuthService gains getUserId(), which decodes userId from the access token. It is used only to scope preferences stored in this browser to the account that chose them, never as an access decision: the token is not verified there, and a forged one would only let its bearer read their own local settings.

* review: read a claim the token actually carries
getUserId read a userId claim that generateAccessToken never signs - it emits { username, did, role, expireAt } - so it returned null for every real session and the theme fell back to the shared key this PR exists to replace. The two specs passed only because they supplied the id themselves. Renamed to getUserKey, reading username, decoded through TextDecoder so a non-ASCII username survives.

setTheme with no account wrote the bare legacy key, which is also every not-yet-chosen user's fallback, so it overwrote what they read; the write is now guarded. The constructor resolved the theme before applyForUser arrived from its round-trip, giving dark-mode users a flash of light on refresh; it seeds from a persisted last-account key, which reset() clears so a shared machine does not keep the previous user's theme after sign-out.

* review: add the real-payload spec for getUserKey
The spec the review asked for: a token built the way generateAccessToken signs one, so reading the wrong claim fails here instead of passing on a fixture that supplies the id itself.
---------
Signed-off-by: Volodymyr Shvets <volodymyr.shvets@climission.com>
* fix(frontend): confirm a button-block decision and restore the row on failure
onSelect submits the decision with an empty success callback, so an accepted Approve or Reject shows nothing at all - indistinguishable from the click not registering. It now confirms by button name.
The failure path is worse. commonVisible is set false optimistically before the request and the error arm never puts it back, so a rejected submit leaves the user with no controls and only a console line. It now restores the row and reports the failure.
Note this does not force the decision buttons to stay hidden after a successful submit. Their visibility is computed from the block state the server pushes back; overriding that client-side would hide a control that may genuinely still be actionable.

* review: undo both optimistic writes on a rejected submit
The review asked for the field value and the IndexedDB hide-events write. Capturing the field value in onSelect does not cover the dialog path: onSelectDialog appends the comment before calling onSelect, so a snapshot taken there already holds it and the retry would still record it twice. The restore point is captured in onSelectDialog and passed in instead.
---------
Co-authored-by: Volodymyr Shvets <volodymyr.shvets@climission.com>
evaluateFieldCondition applied the empty-array guard before dispatching in/not_in. The guard fails closed, which is correct for a comparison with nothing to compare and correct for `in` - nothing is a member of the empty set - but inverted for `not_in`, where a scalar is trivially not in it.

So whenever a source list resolved to empty, a `not_in` condition rejected a document it should have accepted. Moving the dispatch above the guard fixes that arm and leaves every other operator, and the empty-left case, failing closed as before.

Co-authored-by: Volodymyr Shvets <volodymyr.shvets@climission.com>
…chains

* fix(frontend): stop the Policy Wizard producing policies with broken $ref chains
- The wizard resolves each selected schema with getSchemaWithSubSchemas but carries only the parent forward, so nothing verifies that every schema in the referenced $ref chain is present. A schema whose sub-schemas are missing is accepted, and the failure only appears afterwards on the assembled policy as "refers to non-existing schema". The resolved sub-schemas are now kept, the chain is walked before Create Policy, and the unresolved IRIs are named in the dialog.
- Both getSchemaWithSubSchemas subscribes also had a next callback only. A failed lookup left its promise pending: on the selection path Promise.all never settled, selectedSchemas was never assigned and the tree never refreshed, so the wizard just stopped responding. Both sites now resolve with the unresolved schema and report it as a dependency that could not be verified.
- Separately, the schema multiselect used appendTo="body" inside the wizard's DynamicDialog. That puts the overlay outside the dialog's positioning context, so PrimeNG's align pass mispositions it, and with no width bound on the panel it then stretches to the body width - rendering as a full-viewport band over the wizard rather than a dropdown under the field. The overlay is now inline and the panel is capped.

* review: clear failed lookups, drop the stale banner, fix the tokens
- failedSchemaLookups was only ever added to, so one transient network error blocked Create for the life of the dialog even after the user deselected the schema or reselected it successfully. It is cleared on a later success and on deselection.
- missingSubSchemas was recomputed only in onCreate, so the banner kept naming IRIs the user had already fixed; it resets when the selection changes. mintFields guarded - schema.fields undefined rejected the whole Promise.all, so selectedSchemas was never assigned and the tree never refreshed. --guardian-red/-transparent do not exist in the theme; the tokens are --color-accent-red-1/2, as lines 403-404 already used.
- The panel max-width came back out of the shared theme class: it is used by eight other components, and against a panel positioned inside its trigger 100vw never bound anything. The list bound is scoped to this dialog.
---------
Co-authored-by: Volodymyr Shvets <volodymyr.shvets@climission.com>
dependabot Bot and others added 12 commits October 5, 2026 16:14
Bumps the nestjs group with 5 updates:

| Package | From | To |
| --- | --- | --- |
| [@nestjs/common](https://github.com/nestjs/nest/tree/HEAD/packages/common) | `11.2.6` | `11.2.7` |
| [@nestjs/core](https://github.com/nestjs/nest/tree/HEAD/packages/core) | `11.2.6` | `11.2.7` |
| [@nestjs/microservices](https://github.com/nestjs/nest/tree/HEAD/packages/microservices) | `11.2.6` | `11.2.7` |
| [@nestjs/platform-express](https://github.com/nestjs/nest/tree/HEAD/packages/platform-express) | `11.2.6` | `11.2.7` |
| [@nestjs/platform-fastify](https://github.com/nestjs/nest/tree/HEAD/packages/platform-fastify) | `11.2.6` | `11.2.7` |


Updates `@nestjs/common` from 11.2.6 to 11.2.7
- [Release notes](https://github.com/nestjs/nest/releases)
- [Commits](https://github.com/nestjs/nest/commits/v11.2.7/packages/common)

Updates `@nestjs/core` from 11.2.6 to 11.2.7
- [Release notes](https://github.com/nestjs/nest/releases)
- [Commits](https://github.com/nestjs/nest/commits/v11.2.7/packages/core)

Updates `@nestjs/microservices` from 11.2.6 to 11.2.7
- [Release notes](https://github.com/nestjs/nest/releases)
- [Commits](https://github.com/nestjs/nest/commits/v11.2.7/packages/microservices)

Updates `@nestjs/platform-express` from 11.2.6 to 11.2.7
- [Release notes](https://github.com/nestjs/nest/releases)
- [Commits](https://github.com/nestjs/nest/commits/v11.2.7/packages/platform-express)

Updates `@nestjs/platform-fastify` from 11.2.6 to 11.2.7
- [Release notes](https://github.com/nestjs/nest/releases)
- [Commits](https://github.com/nestjs/nest/commits/v11.2.7/packages/platform-fastify)

---
updated-dependencies:
- dependency-name: "@nestjs/common"
  dependency-version: 11.2.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: nestjs
- dependency-name: "@nestjs/core"
  dependency-version: 11.2.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: nestjs
- dependency-name: "@nestjs/microservices"
  dependency-version: 11.2.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: nestjs
- dependency-name: "@nestjs/platform-express"
  dependency-version: 11.2.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: nestjs
- dependency-name: "@nestjs/platform-fastify"
  dependency-version: 11.2.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: nestjs
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps the micro-orm group with 3 updates: [@mikro-orm/core](https://github.com/mikro-orm/mikro-orm), [@mikro-orm/migrations-mongodb](https://github.com/mikro-orm/mikro-orm) and [@mikro-orm/mongodb](https://github.com/mikro-orm/mikro-orm).


Updates `@mikro-orm/core` from 6.6.16 to 6.6.17
- [Release notes](https://github.com/mikro-orm/mikro-orm/releases)
- [Changelog](https://github.com/mikro-orm/mikro-orm/blob/v6.6.17/CHANGELOG.md)
- [Commits](mikro-orm/mikro-orm@v6.6.16...v6.6.17)

Updates `@mikro-orm/migrations-mongodb` from 6.6.16 to 6.6.17
- [Release notes](https://github.com/mikro-orm/mikro-orm/releases)
- [Changelog](https://github.com/mikro-orm/mikro-orm/blob/v6.6.17/CHANGELOG.md)
- [Commits](mikro-orm/mikro-orm@v6.6.16...v6.6.17)

Updates `@mikro-orm/mongodb` from 6.6.16 to 6.6.17
- [Release notes](https://github.com/mikro-orm/mikro-orm/releases)
- [Changelog](https://github.com/mikro-orm/mikro-orm/blob/v6.6.17/CHANGELOG.md)
- [Commits](mikro-orm/mikro-orm@v6.6.16...v6.6.17)

---
updated-dependencies:
- dependency-name: "@mikro-orm/core"
  dependency-version: 6.6.17
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: micro-orm
- dependency-name: "@mikro-orm/migrations-mongodb"
  dependency-version: 6.6.17
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: micro-orm
- dependency-name: "@mikro-orm/mongodb"
  dependency-version: 6.6.17
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: micro-orm
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Signed-off-by: Giuseppe Bertone <giuseppe.bertone@hashgraph.com>
Bumps [@aws-sdk/client-secrets-manager](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/clients/client-secrets-manager) from 3.1140.0 to 3.1145.0.
- [Release notes](https://github.com/aws/aws-sdk-js-v3/releases)
- [Changelog](https://github.com/aws/aws-sdk-js-v3/blob/main/clients/client-secrets-manager/CHANGELOG.md)
- [Commits](https://github.com/aws/aws-sdk-js-v3/commits/v3.1145.0/clients/client-secrets-manager)

---
updated-dependencies:
- dependency-name: "@aws-sdk/client-secrets-manager"
  dependency-version: 3.1145.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [@scalar/nestjs-api-reference](https://github.com/scalar/scalar/tree/HEAD/integrations/nestjs) from 1.2.23 to 1.2.26.
- [Release notes](https://github.com/scalar/scalar/releases)
- [Changelog](https://github.com/scalar/scalar/blob/main/integrations/nestjs/CHANGELOG.md)
- [Commits](https://github.com/scalar/scalar/commits/HEAD/integrations/nestjs)

---
updated-dependencies:
- dependency-name: "@scalar/nestjs-api-reference"
  dependency-version: 1.2.26
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps the langchain group with 4 updates: [@langchain/classic](https://github.com/langchain-ai/langchainjs), [@langchain/core](https://github.com/langchain-ai/langchainjs), [@langchain/openai](https://github.com/langchain-ai/langchainjs) and [@langchain/textsplitters](https://github.com/langchain-ai/langchainjs).


Updates `@langchain/classic` from 1.0.48 to 1.0.52
- [Release notes](https://github.com/langchain-ai/langchainjs/releases)
- [Commits](https://github.com/langchain-ai/langchainjs/compare/@langchain/classic@1.0.48...@langchain/classic@1.0.52)

Updates `@langchain/core` from 1.2.12 to 1.2.14
- [Release notes](https://github.com/langchain-ai/langchainjs/releases)
- [Commits](https://github.com/langchain-ai/langchainjs/compare/@langchain/core@1.2.12...@langchain/core@1.2.14)

Updates `@langchain/openai` from 1.5.13 to 1.6.2
- [Release notes](https://github.com/langchain-ai/langchainjs/releases)
- [Commits](https://github.com/langchain-ai/langchainjs/compare/@langchain/openai@1.5.13...@langchain/openai@1.6.2)

Updates `@langchain/textsplitters` from 1.0.1 to 1.0.2
- [Release notes](https://github.com/langchain-ai/langchainjs/releases)
- [Commits](https://github.com/langchain-ai/langchainjs/compare/@langchain/textsplitters@1.0.1...@langchain/textsplitters@1.0.2)

---
updated-dependencies:
- dependency-name: "@langchain/classic"
  dependency-version: 1.0.52
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: langchain
- dependency-name: "@langchain/core"
  dependency-version: 1.2.14
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: langchain
- dependency-name: "@langchain/openai"
  dependency-version: 1.6.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: langchain
- dependency-name: "@langchain/textsplitters"
  dependency-version: 1.0.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: langchain
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [ws](https://github.com/websockets/ws) and [@types/ws](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/ws). These dependencies needed to be updated together.

Updates `ws` from 8.21.3 to 8.22.0
- [Release notes](https://github.com/websockets/ws/releases)
- [Commits](websockets/ws@8.21.3...8.22.0)

Updates `@types/ws` from 8.18.1 to 8.18.2
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/ws)

---
updated-dependencies:
- dependency-name: "@types/ws"
  dependency-version: 8.18.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
- dependency-name: ws
  dependency-version: 8.22.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 24.13.6 to 24.19.1.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 24.19.1
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [tsc-alias](https://github.com/justkey007/tsc-alias) from 1.9.5 to 1.9.7.
- [Release notes](https://github.com/justkey007/tsc-alias/releases)
- [Commits](justkey007/tsc-alias@v1.9.5...v1.9.7)

---
updated-dependencies:
- dependency-name: tsc-alias
  dependency-version: 1.9.7
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
This removes obsolete @types/mapbox-gl packages and refreshes the package lock metadata for both frontend apps. It also updates related dependency versions, including Node typings and several backend/frontend packages, to match the current workspace installations.

Signed-off-by: Alex Piatakov <alex.piatakov@hashgraph.com>
Bumps [@hiero-ledger/sdk](https://github.com/hiero-ledger/hiero-sdk-js) from 2.88.0 to 2.89.1.
- [Release notes](https://github.com/hiero-ledger/hiero-sdk-js/releases)
- [Changelog](https://github.com/hiero-ledger/hiero-sdk-js/blob/main/CHANGELOG.md)
- [Commits](hiero-ledger/hiero-sdk-js@v2.88.0...v2.89.1)

---
updated-dependencies:
- dependency-name: "@hiero-ledger/sdk"
  dependency-version: 2.89.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* test: read contract token balances from mirror
AccountBalanceQuery no longer works since consensus node 0.77. Read token balances from the mirror node and wait for them to settle, as the mirror node lags consensus.
* test: fix contract balance helper edge cases
Zero-change assertions could pass on a stale mirror read, and custom JSON networks had no mirror node configured. Wait out the mirror lag and accept MIRROR_NETWORK.
---------
Signed-off-by: Alex Piatakov <alex.piatakov@hashgraph.com>
@Pyatakov Pyatakov self-assigned this Oct 5, 2026
Comment thread frontend/src/app/modules/schema-engine/rich-text-editor/markdown.ts Dismissed
Comment thread sustainability-atlas/frontend/lib/format.ts Dismissed
Drop the word trial from the template try page button and its docs.

Signed-off-by: Alex Piatakov <alex.piatakov@hashgraph.com>
@github-actions

github-actions Bot commented Oct 5, 2026 •

Copy link
Copy Markdown

Test Results

 33 files  + 33   66 suites  +66   3m 11s ⏱️ + 3m 11s
 35 tests + 35   35 ✅ + 35  0 💤 ±0  0 ❌ ±0 
173 runs  +173  173 ✅ +173  0 💤 ±0  0 ❌ ±0 

Results for commit 18036ee. ± Comparison against base commit e585ead.

♻️ This comment has been updated with latest results.

dariy-rised and others added 3 commits October 6, 2026 13:07
* fix: improve checkForCircularDependency helper
* fix: add schema defs in response
* fix: circular dependency backend check
* fix: update frontend circular dependency checking
* fix: prevent stale $defs from masking circular dependencies
* fix: update tests
---------
Signed-off-by: Dariy Miseldzhani <dariy.miseldzhani@hashgraph.com>
Co-authored-by: Alex Piatakov <alex.piatakov@hashgraph.com>
…s' to Schema Templates

* test: add failing coverage for per-condition and repeatable-link template locking
* refactor: share condition trigger signature via SchemaHelper
* feat: lock individual conditions in schema templates
* feat: lock repeatable links in schema templates
* fix: mono font in textarea schema form
* feat: add/remove/content diffs for schema template conditions and repeatable links
* docs: update related docs
* fix: show all schema template diff changes and hide remove icons in template config
* fix: template update diff for conditions and repeatable links
* fix: scope per-condition lock to trigger/targets, block adding fields to locked conditions
* refactor: drop leftover bare block and remove deprecated conditionTriggerSignature and findMatchingConditionIndex
---------
Signed-off-by: Dariy Miseldzhani <dariy.miseldzhani@hashgraph.com>
* chore: version bump 3.7.1
Update the workspace package versions and lock files from 3.7.1-rc to 3.7.1 across all services and frontend apps in preparation for the next release.
---------
Signed-off-by: Alex Piatakov <alex.piatakov@hashgraph.com>
@Pyatakov
Pyatakov marked this pull request as ready for review October 6, 2026 16:38
@Pyatakov
Pyatakov requested review from a team as code owners October 6, 2026 16:38
@Pyatakov
Pyatakov merged commit 2e9bd2e into main Oct 6, 2026
43 of 44 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.