mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-10-26 05:30:18 +00:00
60fcfe54d37e02ea02419e454b80980331dc65d7
1995 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
60fcfe54d3 |
chore(deps): bump the all-actions group across 1 directory with 4 updates (#1964)
Bumps the all-actions group with 4 updates in the / directory: [github/codeql-action/init](https://github.com/github/codeql-action), [github/codeql-action/autobuild](https://github.com/github/codeql-action), [github/codeql-action/analyze](https://github.com/github/codeql-action) and [github/codeql-action/upload-sarif](https://github.com/github/codeql-action). Updates `github/codeql-action/init` from 4.38.0 to 4.38.1 - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/b96794f015dfd88f77b49b1c93e0fa7110f94c63...1c5b675653bb5c22dbe9b12b556ec555138e09fd) Updates `github/codeql-action/autobuild` from 4.38.0 to 4.38.1 - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/b96794f015dfd88f77b49b1c93e0fa7110f94c63...1c5b675653bb5c22dbe9b12b556ec555138e09fd) Updates `github/codeql-action/analyze` from 4.38.0 to 4.38.1 - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/b96794f015dfd88f77b49b1c93e0fa7110f94c63...1c5b675653bb5c22dbe9b12b556ec555138e09fd) Updates `github/codeql-action/upload-sarif` from 4.38.0 to 4.38.1 - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/b96794f015dfd88f77b49b1c93e0fa7110f94c63...1c5b675653bb5c22dbe9b12b556ec555138e09fd) --- updated-dependencies: - dependency-name: github/codeql-action/init dependency-version: 4.38.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-actions - dependency-name: github/codeql-action/autobuild dependency-version: 4.38.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-actions - dependency-name: github/codeql-action/analyze dependency-version: 4.38.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-actions - dependency-name: github/codeql-action/upload-sarif dependency-version: 4.38.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-actions ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|
|
38959e1ea8 |
chore(deps-dev): bump the all-npm-root group with 2 updates (#1961)
Bumps the all-npm-root group with 2 updates: [@commitlint/cli](https://github.com/conventional-changelog/commitlint/tree/HEAD/@commitlint/cli) and [@commitlint/config-conventional](https://github.com/conventional-changelog/commitlint/tree/HEAD/@commitlint/config-conventional). Updates `@commitlint/cli` from 21.2.2 to 21.2.3 - [Release notes](https://github.com/conventional-changelog/commitlint/releases) - [Changelog](https://github.com/conventional-changelog/commitlint/blob/master/@commitlint/cli/CHANGELOG.md) - [Commits](https://github.com/conventional-changelog/commitlint/commits/v21.2.3/@commitlint/cli) Updates `@commitlint/config-conventional` from 21.2.2 to 21.2.3 - [Release notes](https://github.com/conventional-changelog/commitlint/releases) - [Changelog](https://github.com/conventional-changelog/commitlint/blob/master/@commitlint/config-conventional/CHANGELOG.md) - [Commits](https://github.com/conventional-changelog/commitlint/commits/v21.2.3/@commitlint/config-conventional) --- updated-dependencies: - dependency-name: "@commitlint/cli" dependency-version: 21.2.3 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: all-npm-root - dependency-name: "@commitlint/config-conventional" dependency-version: 21.2.3 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: all-npm-root ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|
|
e5d256f8da |
chore(deps): bump the all-npm-backend group in /backend with 7 updates (#1963)
Bumps the all-npm-backend group in /backend with 7 updates: | Package | From | To | | --- | --- | --- | | [ldapts](https://github.com/ldapts/ldapts) | `9.0.0` | `9.2.0` | | [systeminformation](https://github.com/sebhildebrandt/systeminformation) | `5.33.10` | `5.33.13` | | [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) | `26.5.1` | `26.6.2` | | [eslint](https://github.com/eslint/eslint) | `10.10.0` | `10.11.0` | | [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest) | `5.0.0` | `5.0.1` | | [@aws-sdk/client-ecr](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/clients/client-ecr) | `3.1132.0` | `3.1136.0` | | [@aws-sdk/client-s3](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/clients/client-s3) | `3.1132.0` | `3.1136.0` | Updates `ldapts` from 9.0.0 to 9.2.0 - [Release notes](https://github.com/ldapts/ldapts/releases) - [Changelog](https://github.com/ldapts/ldapts/blob/main/CHANGELOG.md) - [Commits](https://github.com/ldapts/ldapts/compare/v9.0.0...v9.2.0) Updates `systeminformation` from 5.33.10 to 5.33.13 - [Release notes](https://github.com/sebhildebrandt/systeminformation/releases) - [Changelog](https://github.com/sebhildebrandt/systeminformation/blob/master/CHANGELOG.md) - [Commits](https://github.com/sebhildebrandt/systeminformation/compare/v5.33.10...v5.33.13) Updates `@types/node` from 26.5.1 to 26.6.2 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) Updates `eslint` from 10.10.0 to 10.11.0 - [Release notes](https://github.com/eslint/eslint/releases) - [Commits](https://github.com/eslint/eslint/compare/v10.10.0...v10.11.0) Updates `vitest` from 5.0.0 to 5.0.1 - [Release notes](https://github.com/vitest-dev/vitest/releases) - [Changelog](https://github.com/vitest-dev/vitest/blob/main/docs/releases.md) - [Commits](https://github.com/vitest-dev/vitest/commits/v5.0.1/packages/vitest) Updates `@aws-sdk/client-ecr` from 3.1132.0 to 3.1136.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-ecr/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-js-v3/commits/v3.1136.0/clients/client-ecr) Updates `@aws-sdk/client-s3` from 3.1132.0 to 3.1136.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-s3/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-js-v3/commits/v3.1136.0/clients/client-s3) --- updated-dependencies: - dependency-name: ldapts dependency-version: 9.2.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-npm-backend - dependency-name: systeminformation dependency-version: 5.33.13 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-npm-backend - dependency-name: "@types/node" dependency-version: 26.6.2 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: all-npm-backend - dependency-name: eslint dependency-version: 10.11.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: all-npm-backend - dependency-name: vitest dependency-version: 5.0.1 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: all-npm-backend - dependency-name: "@aws-sdk/client-ecr" dependency-version: 3.1136.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-npm-backend - dependency-name: "@aws-sdk/client-s3" dependency-version: 3.1136.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-npm-backend ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|
|
7767fd6f93 |
fix(gitops): name the unresolved artifact identity and drop the stale source report (#1960)
The operator source-accept route seeds an unresolved artifact set and no producer resolves one for a Git-managed Blueprint, so authorization refused with a registry-worded message that named the wrong cause. Refuse before evaluating registry readiness and report the artifact identity, on both the route and the placement facet, through one shared predicate. Placement also reported source acceptance pending from a rollout candidate bound to the previous authorized generation, which no generation is waiting on. Only a live staged generation counts as that decision now. Source acceptance stays available while a Blueprint is disabled, matching the backend, and the dispatch-failure note no longer points at a surface without a retry control. |
||
|
|
b27fbeeb46 |
feat(gitops): add decomposed approval actions for Git-managed Blueprints (#1959)
* feat(gitops): add decomposed approval actions for Git-managed Blueprints Source acceptance, placement approval, and rollout authorization are now writable as separate operator actions under /api/gitops/applications/:id. Placement approval re-runs the Blueprint preview and binds the reviewed plan (409 with a fresh preview on drift); rollout authorization evaluates registry preflight, records operator authority, and dispatches the accepted generation through the shared boundary. The placement facet reports a Git-managed candidate with no placement approval as placement review pending, and a candidate newer than the accepted generation as source acceptance pending. * feat(gitops): surface the outstanding authority step on the app view and sheet The GitOps application view and the Blueprint sheet's GitOps section now offer the outstanding authority step as one action: Accept source, Review and approve placement, or Authorize rollout. Placement review opens the rollout preview, which records the placement approval for a Git-managed Blueprint instead of an inline apply, so the composed source and artifact evidence is reviewed there. * docs(gitops): document the decomposed approval actions The Blueprint page describes where each step appears, what placement approval binds, and how a blocked preflight or a refused target start reads; the GitOps portfolio page notes the action in the application view. |
||
|
|
a881e425f8 |
feat(gitops): compose the authority evidence into the Blueprint rollout preview (#1957)
The Blueprint rollout preview now embeds the canonical GitOps revision projection and binds the confirmation to a digest of the authority evidence it displays: the recorded approval refs, the rollout generation, and the source, artifact, placement and rollout facet identities. Apply refuses with PREVIEW_STALE unless the intent fingerprint and the evidence digest both still match, so an acceptance, artifact identity, placement approval, preflight result or rollout authorization that moved under the preview cannot execute. The dialog renders the composed evidence through the same chips, cards and per-target rendering the GitOps application view uses, keeps the legacy combined approval separate from the decomposed steps, and refreshes or closes on a stale refusal instead of re-arming a preview the server already rejected. Volatile observations (timestamps, retry counters, per-target runtime and health readings) are excluded from the digest so a steady-state application does not read as permanently stale. |
||
|
|
4453ca552e |
fix(e2e): ride out the shared API limiter in the sidebar truncation spec (#1958)
The spec shares the app's per-minute API rate limiter with every other spec in the suite, so a late-run burst can answer 429. Its setup treated that as a hard failure, and a rate-limited bootstrap leaves the shell without its data and does not recover on its own, so the retries then failed waiting for the dashboard. Retry the login and dashboard readiness, the stack creates (only a 429 is retried; any other status still fails on the first answer) and the post-setup reload, waiting out the window between attempts. The describe gets a longer test budget so a saturated minute cannot time the retries out. The app's rate-limit behavior is unchanged. |
||
|
|
46937e7db1 |
feat(gitops): open each portfolio application in a read-only application view (#1956)
* test(gitops): cover every pending approval trigger and guard liveSourceFacet Parametrize the approval chip tests over each pending and not-applicable trigger status, and give liveSourceFacet the same missing-facets guard its sibling facet readers already have. * feat(gitops): open each portfolio application in a read-only application view Portfolio rows now drill into a single-application view inside the GitOps workplace, read from the hub-owned application endpoint: identity, attention reasons, decomposed authority chips, the source, artifact, placement and rollout facets, every target, classified drift, and caveats. The view hands off to the owning surface (stack Git panel or Blueprint deployments) for any action. The open application lives in a query parameter the GitOps view owns, so the view is linkable and Back returns to the filtered list. The phone screen reuses the same view. * test(gitops): cover the application view, its data hook, and navigation Also keeps a facet or target whose status this build does not recognize visible as an explicit unrecognized state in the application view, instead of dropping the card, and adds first render coverage for the placement and rollout badge arms. * fix(gitops): classify application read failures precisely and keep the view linkable A node too old to answer, a malformed link, and an unreadable application now read as three different states; retry stays available where a later answer can differ. A successful answer the view cannot read is reported instead of rendering blank, unknown target modes, postures and artifact kinds render as unrecognized, and a failed refresh keeps a retry beside the last-known banner. A search debounce that fires after a row was opened no longer replaces the application URL. Documents the application view. * fix(gitops): return to the portfolio from GitOps navigation and keep phone shell actions Selecting GitOps in the navigation, or following a contextual GitOps indicator, while an application view is open now returns to the list instead of doing nothing. The phone application view keeps the shell actions (notifications, more menu) in its top bar. * refactor(gitops): tidy the application view navigation and evidence line Narrows two hand-off helpers to module scope, reads the application id with a plain URLSearchParams, folds a duplicated window check, and moves the partial-evidence sentence into a small helper. * test(gitops): pin the liveSourceFacet guard and the view's unrecognized and error states * docs(gitops): show the application view on the portfolio page |
||
|
|
85f5246445 |
feat(gitops): add the GitOps portfolio workplace (#1954)
* feat(gitops): add the hub-owned GitOps portfolio read API
Adds the canonical aggregate read surface behind the GitOps workplace:
- GET /api/gitops/applications: one bounded response covering Direct
applications from the hub and every reachable node, plus hub-owned
Blueprint applications. Remote legs probe each node's git-sources with
a bounded timeout, rewrite node identity into hub numbering, and
re-authorize every row against the calling user; unreachable or
unsupported nodes become named coverage entries rather than vanishing
rows.
- GET /api/gitops/applications/🆔 the row plus the canonical
projection, the shared single-application contract (remote ids resolve
through a single-node probe; legacy ids resolve by stack name).
- Server-owned attention classification (services/gitops/attention.ts):
a closed reason union with failure/pending tone, computed once from
canonical facets, so no client re-derives "needs attention".
- Per-application posture (failed/attention/in_progress/converged/
converged_qualified/unknown) from the same facets; qualified
convergence never claims exactness, and an unrecognized status from a
newer node is reported as unknown evidence instead of being
reinterpreted.
- The surface is Community and hub-only: the prefix is listed in
HUB_ONLY_PREFIXES and node names are withheld from callers without
node:read.
* feat(gitops): add the GitOps workplace view and phone screen
A first-class GitOps destination next to Fleet (launcher, command
palette, direct URL /nodes/<hub>/gitops, hub-only via HUB_ONLY_VIEWS):
- Portfolio masthead: verdict word, application/attention/drifted/
pending/converged counts, and an honesty line that names unreachable
or too-old nodes and says when a failed refresh left last-known data
on screen.
- Attention queue: one entry per canonical reason with the explanation
inline, failures sorted ahead of pending decisions, independent of the
list filters so narrowing the table cannot hide an exception.
- Application table with server-side search, filters (attention, target
mode, node involvement, drift class, evidence), cursor pagination, and
drill-down hand-offs: Direct rows open the stack's Git source panel on
the owning node, Blueprint rows land on Fleet deployments.
- Bespoke phone screen on the shared data hook: masthead-led, chip
filters, inline attention reasons, >=44px targets.
- Refresh is event-driven off the gitops invalidate channel (debounced),
with URL-synced filters that survive reloads; the router no longer
strips query parameters owned by the view.
- Dashboard badges and the sidebar pending indicator link into the
workplace through one shared navigate helper.
* docs(gitops): document the portfolio workplace
Adds the operator page for the GitOps portfolio (what the page answers,
how to read the masthead and queue, filters, freshness honesty) with a
screenshot, and registers it in the docs navigation.
* fix(preferences): accept the gitops destination as a quick-link id
The GitOps nav entry is quick-link eligible, but the backend enum that
validates saved quick-link preferences still listed only the previous
ids, so the preference-parity pretest rejected the value-set drift.
* chore(gitops): drop imports orphaned by the review pass
Three dead imports left behind when the aggregator and route code moved
during review: an unused test import, and two unused type imports.
* refactor(gitops): rebuild the workplace on the shared page patterns
The portfolio page was visually drifting from the rest of the app. It now
uses the same building blocks as the Dashboard, Fleet, and Security pages:
- Masthead rebuilt on the Dashboard/Fleet recipe: beveled card with the
accent tint wash, a rail that shimmers while the data is live, the state
word with a mono meta line, stat tiles (applications, pending, drifted,
converged with a qualified sub-line), the attention count in the alerts
slot, and a live ticker so "updated Xs ago" keeps moving. A failed
refresh reuses the Dashboard's stale chip.
- Filter row matches the Security Images and Fleet tabs: the collapsible
search affordance (square outline button expanding into a focused input)
plus flat combobox filters on one wrapping row, replacing the bespoke
chip set.
- Table rebuilt on the shared table shell: beveled card, ScrollArea so the
scrolling stays inside the table, tracked-mono header cells, hover-tinted
rows with the application name as the drill-down target, and the
pagination footer in the standard ghost-button style.
- The e2e spec drives the new control shapes.
* test(nav): poll for the settled Navigate panel size
The panel-size assertion read scrollHeight/clientHeight once, immediately
after the panel opened. The popper's available-height variable is applied
after opening, so a single read can observe the pre-positioning clamp and
report a settled panel as clipped; on CI this failed intermittently while
the same check passes consistently once the layout settles. Poll until
the metrics stabilize, which is what the assertion actually means.
|
||
|
|
6345e695c4 |
fix(ui): wrap modal titles on word boundaries (#1950)
Modal headers split long titles at arbitrary character boundaries, rendering fragments like "Prune Sencho-manage" / "d images". HeaderShell, which backs all four modal header variants, applied break-all (breaks between any two characters) plus line-clamp-3 (could hide destructive-confirm context). Replace break-all with break-words (normal words wrap only on word boundaries; a long unbroken token still breaks as a last resort, so no horizontal overflow) and add text-balance (multi-line titles wrap into balanced lines, eliminating the orphaned single-letter second line). Remove line-clamp-3: titles are short enough that clamping only risked hiding context, and the dialog container caps at max-h-[85dvh] with a scrolling body, so full titles cannot push actions off-screen. Covered by class-contract tests across all four header variants and a Playwright spec asserting computed wrapping styles, dialog containment, and balanced line wrapping at the confirm-dialog geometry. |
||
|
|
5981355c27 |
feat(gitops): render decomposed approval authority and placement/rollout state (#1955)
Add placement and rollout facet state maps with lookup views for wire statuses from newer nodes, a presentational approval-chips component that reads granted steps only from recorded refs and pending steps only from facet statuses, and render-only wiring in the blueprint detail, git source, and drift panels. Preflight-blocked placements now show the redacted server reason on all three surfaces. No endpoints, no mutations, no approval-flow changes. |
||
|
|
489e1a9a9a |
feat(blueprints): detect and enforce runtime digest drift (#1949)
* feat(blueprints): detect and enforce runtime digest drift Compare frozen expected artifact identity to live container digests so tag movement surfaces as drift, and Enforce repairs with digest pins (--pull never) without rewriting authored compose. Unreachable or incomplete observation stays unverified and never auto-corrects. * fix(security): keep digest overlay in tmpdir and sanitize observation logs Write Compose digest-pin overlays under os.tmpdir() instead of the stack directory so path expressions are not stack-name tainted, and log runtime observation failures with a fixed format string plus sanitized stack name. * fix(security): create digest overlay via exclusive mkdtemp Avoid CodeQL insecure-temporary-file by writing the Compose digest-pin overlay into an exclusive mkdtemp directory with wx create, then removing the whole directory after deploy. * fix: compare Blueprint digest drift by per-target platform identity Enforce now routes container repair separately from digest pins, keeps compose.override.yml on overlay deploys, and fails closed when the target platform is unknown. * test: expect platformVariants on single-platform digest resolve * test: drop unused seedApp binding in digest-repair suite * fix: drop redundant platformLabel nullish coalesce after guard * fix(blueprints): accept override-only services in apply-local pin guard * fix(blueprints): freeze and repair remote digests from the leaf Hub digest freeze and Enforce repair now fetch the target node's rendered Compose model and Docker platform over the authenticated proxy, so a remote compose_dir string is never treated as a hub-local path. apply-local pin validation runs after write against the rendered multi-file model. |
||
|
|
2baf4e32ee |
fix(notifications): redact webhook URLs on channel reads (#1951)
Discord, Slack, generic webhook, and ntfy destinations embed tokens in the URL. Return origin-only placeholders on list responses so any authenticated reader cannot reuse those credentials outside Sencho. |
||
|
|
23baec2a7d |
fix(gitops): prune provider webhook endpoints when a git source or stack is removed (#1940)
* fix(gitops): prune provider webhook endpoints when a git source or stack is removed Detaching a git source, deleting its stack, or deleting a single webhook endpoint left the git_provider_endpoints rows (which hold encrypted secrets) and their git_provider_deliveries history behind as orphans. FK enforcement is off, so the schema's ON DELETE CASCADE never fires; delete the deliveries explicitly, then the endpoints, transactionally, at all three removal points. * test(gitops): assert provider-hook rotate does not prune endpoints Pins the rotate path as update-only so a future prune regression cannot quietly delete endpoints or deliveries when rotating a webhook secret. |
||
|
|
2fb2c9116c |
feat(gitops): block Blueprint place until private registry targets are ready (#1946)
* feat(gitops): block Blueprint place until private registry targets are ready Evaluate per-target registry readiness before minting rollout authorization, persist blocked and ready evidence for derive, remint on fingerprint drift, and refuse Direct deploy when private hosts cannot be proven ready. * fix(gitops): let Direct apply tests override registry preflight Unresolved artifact fixtures were failing closed before Compose in git-source-service tests. Move readiness test hooks onto the evaluator module and override evaluation for Direct suites that are not about registry readiness. * fix(gitops): stub registry preflight on apply-recovery mocks The Direct apply recovery suite mocks GitOpsTransitions without the new preflight methods, so deploy-after-apply crashed before Compose. Stub the transitions and override the evaluator for non-registry fixtures. * fix(gitops): clear code-quality findings on preflight serialization Use a token for inflight evaluation cleanup instead of comparing promises, and drop a redundant summary guard already narrowed by the Blueprint detail branch. * fix(gitops): distinguish unevaluated registry preflight from a failed probe Surface a dedicated reason when placement has no stored evidence yet, document the application-scoped inbound envelope host set, and note the first-run vs failed-evaluation distinction in operator docs. |
||
|
|
591b06220b |
feat(registry): seal private-registry credentials for HTTP delivery (#1945)
* feat(registry): seal private-registry credentials for HTTP delivery Pin each remote node's X25519 sealing key on first use and deliver sealed auths so private-image Compose ops work without a confidential transport hop. * fix(registry): avoid TOCTOU when loading the sealing key file Read and exclusive-create the key without existsSync races so CodeQL js/file-system-race no longer flags the seal helper or its unit test. * fix(registry): read sealing key test fixture through one fd Avoid a check-then-read race in the unit test that CodeQL still flagged after the helper-side exclusive-create fix. |
||
|
|
a53fc42b86 |
fix(catalog): use js-yaml namespace import for ESM v5 (#1942)
js-yaml 5 removed the ESM default export, so the website-catalog scripts failed Catalog Drift Check with a SyntaxError on import. |
||
|
|
23d232b7bb |
chore: ignore local research/ and .opencode/ directories (#1944)
Keep competitor research clones and OpenCode tooling out of the tracked tree. |
||
|
|
9cbb5f8ceb |
feat(gitops): authorize Git-managed Blueprint rollout generations
Mint rollout_authorization with preflight fingerprinting, place from accepted generation materialization (never snapshot YAML), and project placement/rollout derive facets. Restart reconstructs remaining sequential targets without replaying acked ones under the live auth ref. |
||
|
|
c02dfa4fdd |
feat: listen with HTTPS so Pilot credential delivery stays confidential (#1943)
* feat: listen with HTTPS so Pilot credential delivery stays confidential When the control instance is not behind a reverse proxy, cert and key files opt the process into an HTTPS listener so Pilot tunnels are encrypted. Enrollment emits an https URL and an optional CA bind-mount. Pilot agents ignore those cert files so loopback replay stays HTTP. * fix: verify native TLS health probes against the public URL Keep certificate verification on for the loopback HEALTHCHECK. Dial 127.0.0.1, check identity against SENCHO_PUBLIC_URL, and trust SENCHO_TLS_CA_FILE when set. * fix: trust HEALTHCHECK CA via the process store, not the request Private-issuer PEM is added with tls.setDefaultCACertificates so the loopback HTTPS probe never puts file bytes into https.get options. |
||
|
|
20d0db4691 |
feat(gitops): add rollout generations and Inline placement approval
Add immutable gitops_rollout_generations and mint placement_approval on Inline Confirm Apply with a legacy_inline generation that freezes the intent strategy. Cut the reconciler over to placementEffectCompatible, migrate existing Inline apps with non-authoritative legacy_combined, and keep Git-managed place blocked until the follow-up authorization PR. |
||
|
|
39e0af9fa7 |
feat(registry): Pilot private-registry parity - target facts, hub authority, auto-update (#1930)
* feat(registry): Pilot private-registry parity — target facts, hub authority, auto-update - Target facts contract (AUD-14, AUD-24): remote image-update observation tokens with 5-min TTL, one-use replay, sequential per-target execution - Hub check authority and storage (AUD-11, AUD-21): overlay status endpoint, dual-scanner ownership matrix (hub vs target), independent enabled states - Checked auto-update coordinator (AUD-22, AUD-28): awaited post-mutation verification, fleet recheck only on confirmed success, stale-observation refusal - Registry transport safety (AUD-29, AUD-33): allowlisted public HTTPS realms, redirect-safety, no credential leak on blocked/unsafe addresses - Frontend consumers: UpdatesSection dual-scanner controls, useImageUpdates hook, Security OverviewTab target recheck, Masthead stats integration - Docs: private-registries.mdx updated with exact-proof delivery, capability requirements, and troubleshooting for transport, credential, and capability mismatches * test: seed facts API token via shared test helper The image-update facts boundary test inlined the production token-hash construction (generateApiToken plus sha256) to seed its full-admin token row, which re-triggered the recurring js/insufficient-password-hash finding for test files that hash API tokens. Delegate to the existing API token test helper instead: it mirrors the production construction and its location already carries the documented false-positive determination. * fix(security): sanitize auto-update failure logs against injection Strip control characters from stack names and error messages before console.error so registry-sourced strings cannot forge log lines. * fix(security): clear remaining CodeQL log and path injection alerts Sanitize failure logs across registry delivery, safe-probe, fleet restore, webhooks, and scheduler. Retarget authoredComposeArgs path barriers to the compose root so CodeQL credits the containment check. |
||
|
|
9d0dbebbfa |
feat(gitops): SOPS/age repository secrets without durable plaintext (#1935)
* feat(gitops): SOPS/age repository secrets without durable plaintext Add age-only SOPS detection, node-scoped identity management, and a short-lived decrypt overlay for Git validate, apply, deploy, and recovery paths. Ciphertext stays in Git and on disk; generations carry redacted secretCapability metadata. * fix(gitops): repair SOPS route and GitSourcePanel test failures Mock GitSourceSecretsSection in panel tests and align SOPS identity route tests with viewer auth, upsert mocking, and application scoping. * fix(gitops): repair deploy guard, schema test, and deploy-retry manifest Use overlayDir only for env blocking during Git overlay deploys, register gitops_sops_identities in the schema inventory test, and rebuild manifest from revalidation when deploy retry runs before on-disk manifest exists. * fix(gitops): clear unused imports flagged by ESLint Remove an unused SecretCapability type import and stop destructuring destroyGitOpsOverlay where the finally block re-imports it separately. * fix(gitops): harden overlay paths for CodeQL path-injection checks Validate stack names, operation ids, and node ids before building overlay paths, confine copy and sweep operations with isPathWithinBase, and register validation helpers as CodeQL path-injection barriers. * fix(gitops): add inline CodeQL path barriers on overlay sinks Use path.basename plus resolve/startsWith checks at each overlay filesystem call, matching the managed-path pattern, and exclude the intentional Git ciphertext decrypt write from js/http-to-file-access. * fix(gitops): confine overlay source roots and exclude from CodeQL Validate source trees against compose and git-managed stack roots, align the overlay integration test with that layout, and paths-ignore the decrypt overlay module where inline barriers still do not satisfy CodeQL. * fix(gitops): bind SOPS recovery overlay to generation metadata Rollback decrypts from the target generation's secret capability, remaps captured compose paths onto the overlay, and refuses ciphertext manual deploy or update. Detect dotenv and INI SOPS documents instead of treating them as plaintext. * fix(gitops): stub getDb in ComposeService unit tests The SOPS refuse guard reads GitOps state through GitOpsStore. The subprocess mock had no getDb, so deploy and update tests threw before Compose ran. |
||
|
|
a698b6d983 |
feat: GitOps executable artifact identity (Direct nodes) (#1937)
* feat: add GitOps executable artifact identity for Direct nodes Resolve registry digests on accept, probe stale tag movement without advancing expected pointers, observe runtime images after deploy, and surface artifact state separately from source and runtime in the UI. * fix: unblock CI lint and Git Source artifact card types Drop the unused observation encoder import and narrow latestEvidence before rendering so backend lint and frontend tsc pass. * fix: keep GitOps deploy success independent of runtime observation Post-deploy artifact inspection is fire-and-forget and records unavailable on Docker or target errors, so a successful deploy is never reported as a failure. Per-service resolve and observe run in parallel, and the Git Source card shows the frozen expected identity. |
||
|
|
e62b059abf |
feat(gitops): native Git provider webhook ingestion (#1934)
* feat(gitops): native Git provider webhook ingestion Add hub-facing provider hook ingest with per-stack endpoint CRUD, signature verification for major Git hosts, and GitOps reconcile via provider_event. * fix(frontend): drop unused isDarkMode prop from provider hooks card * fix(frontend): stop destructuring unused isDarkMode in GitSourcePanel * fix(gitops): log remote hook forwards and tighten provider ingest Record GitHub-family event actions, skip noisy PR sub-actions, treat replayed deliveries as duplicates without a second reconcile, and drop the GitLab HMAC header claim that the real provider does not send. * fix(gitops): retry failed provider deliveries on redelivery Do not treat processing_failed as a settled ingest state, so a host redelivery can run reconcile again. Deduplicate the delivery lookup helpers. * fix(gitops): align provider delivery headers and drop unused rate_limited Use native GitLab/Gitea/Forgejo delivery-id headers in adapters and ingest, assert stored delivery ids, and remove the unreachable rate_limited state. |
||
|
|
34672b1bc6 |
chore(deps): bump the all-npm-backend group across 1 directory with 12 updates (#1933)
* chore(deps): bump the all-npm-backend group across 1 directory with 12 updates Bumps the all-npm-backend group with 12 updates in the /backend directory: | Package | From | To | | --- | --- | --- | | [compression](https://github.com/expressjs/compression) | `1.8.1` | `1.8.2` | | [cron-parser](https://github.com/harrisiirak/cron-parser) | `5.10.0` | `5.10.1` | | [multer](https://github.com/expressjs/multer) | `2.3.0` | `2.4.0` | | [systeminformation](https://github.com/sebhildebrandt/systeminformation) | `5.33.8` | `5.33.10` | | [tar-stream](https://github.com/mafintosh/tar-stream) | `3.2.0` | `3.2.1` | | [yaml](https://github.com/eemeli/yaml) | `2.9.0` | `2.9.1` | | [zod](https://github.com/colinhacks/zod) | `4.5.4` | `4.6.5` | | [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) | `26.4.1` | `26.5.1` | | [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint) | `8.69.0` | `8.70.0` | | [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest) | `4.1.11` | `5.0.0` | | [@aws-sdk/client-ecr](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/clients/client-ecr) | `3.1127.0` | `3.1132.0` | | [@aws-sdk/client-s3](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/clients/client-s3) | `3.1127.0` | `3.1132.0` | Updates `compression` from 1.8.1 to 1.8.2 - [Release notes](https://github.com/expressjs/compression/releases) - [Changelog](https://github.com/expressjs/compression/blob/master/HISTORY.md) - [Commits](https://github.com/expressjs/compression/compare/v1.8.1...v1.8.2) Updates `cron-parser` from 5.10.0 to 5.10.1 - [Release notes](https://github.com/harrisiirak/cron-parser/releases) - [Changelog](https://github.com/harrisiirak/cron-parser/blob/master/CHANGELOG.md) - [Commits](https://github.com/harrisiirak/cron-parser/compare/v5.10.0...v5.10.1) Updates `multer` from 2.3.0 to 2.4.0 - [Release notes](https://github.com/expressjs/multer/releases) - [Changelog](https://github.com/expressjs/multer/blob/main/CHANGELOG.md) - [Commits](https://github.com/expressjs/multer/compare/v2.3.0...v2.4.0) Updates `systeminformation` from 5.33.8 to 5.33.10 - [Release notes](https://github.com/sebhildebrandt/systeminformation/releases) - [Changelog](https://github.com/sebhildebrandt/systeminformation/blob/master/CHANGELOG.md) - [Commits](https://github.com/sebhildebrandt/systeminformation/compare/v5.33.8...v5.33.10) Updates `tar-stream` from 3.2.0 to 3.2.1 - [Commits](https://github.com/mafintosh/tar-stream/compare/v3.2.0...v3.2.1) Updates `yaml` from 2.9.0 to 2.9.1 - [Release notes](https://github.com/eemeli/yaml/releases) - [Commits](https://github.com/eemeli/yaml/compare/v2.9.0...v2.9.1) Updates `zod` from 4.5.4 to 4.6.5 - [Release notes](https://github.com/colinhacks/zod/releases) - [Commits](https://github.com/colinhacks/zod/compare/v4.5.4...v4.6.5) Updates `@types/node` from 26.4.1 to 26.5.1 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) Updates `typescript-eslint` from 8.69.0 to 8.70.0 - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.70.0/packages/typescript-eslint) Updates `vitest` from 4.1.11 to 5.0.0 - [Release notes](https://github.com/vitest-dev/vitest/releases) - [Changelog](https://github.com/vitest-dev/vitest/blob/main/docs/releases.md) - [Commits](https://github.com/vitest-dev/vitest/commits/v5.0.0/packages/vitest) Updates `@aws-sdk/client-ecr` from 3.1127.0 to 3.1132.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-ecr/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-js-v3/commits/v3.1132.0/clients/client-ecr) Updates `@aws-sdk/client-s3` from 3.1127.0 to 3.1132.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-s3/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-js-v3/commits/v3.1132.0/clients/client-s3) --- updated-dependencies: - dependency-name: compression dependency-version: 1.8.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-npm-backend - dependency-name: cron-parser dependency-version: 5.10.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-npm-backend - dependency-name: multer dependency-version: 2.4.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-npm-backend - dependency-name: systeminformation dependency-version: 5.33.10 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-npm-backend - dependency-name: tar-stream dependency-version: 3.2.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-npm-backend - dependency-name: yaml dependency-version: 2.9.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-npm-backend - dependency-name: zod dependency-version: 4.6.5 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-npm-backend - dependency-name: "@types/node" dependency-version: 26.5.1 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: all-npm-backend - dependency-name: typescript-eslint dependency-version: 8.70.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: all-npm-backend - dependency-name: vitest dependency-version: 5.0.0 dependency-type: direct:development update-type: version-update:semver-major dependency-group: all-npm-backend - dependency-name: "@aws-sdk/client-ecr" dependency-version: 3.1132.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-npm-backend - dependency-name: "@aws-sdk/client-s3" dependency-version: 3.1132.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-npm-backend ... Signed-off-by: dependabot[bot] <support@github.com> * chore(deps): fix type fallout from vitest 5 and tar-stream 3.2.1 bumps tar-stream 3.2.1 bundles its own type definitions that import from streamx, shadowing @types/tar-stream; the pinned streamx 2.25.0 ships no types, so Pack/Extract lost their stream methods. Add streamx ^2.28.1 (which bundles types) as a direct dependency. vitest 5 made vite a true peer dependency, so add vite ^8.3.0 as a devDependency to match the frontend. Adjust three test data handlers for streamx's unknown-typed data event. * chore(deps): drop redundant @types/tar-stream, narrow data handlers tar-stream 3.2.1 bundles its own type definitions, so the @types package is dead weight that could re-enter resolution later. Type the test data handlers as unknown with Buffer.isBuffer narrowing instead of casting. --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Anso <dev@anso.codes> |
||
|
|
7d40622d09 |
chore(deps): bump the all-npm-frontend group across 1 directory with 15 updates (#1932)
Bumps the all-npm-frontend group with 15 updates in the /frontend directory: | Package | From | To | | --- | --- | --- | | [cronstrue](https://github.com/bradymholt/cRonstrue) | `3.24.0` | `3.26.0` | | [lucide-react](https://github.com/lucide-icons/lucide/tree/HEAD/packages/lucide-react) | `1.41.0` | `1.46.0` | | [motion](https://github.com/motiondivision/motion) | `13.2.0` | `13.3.0` | | [react](https://github.com/react/react/tree/HEAD/packages/react) | `19.2.8` | `19.3.0` | | [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react) | `19.2.18` | `19.3.0` | | [react-dom](https://github.com/react/react/tree/HEAD/packages/react-dom) | `19.2.8` | `19.3.0` | | [@types/react-dom](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react-dom) | `19.2.7` | `19.3.0` | | [react-is](https://github.com/react/react/tree/HEAD/packages/react-is) | `19.2.8` | `19.3.0` | | [tailwind-merge](https://github.com/dcastil/tailwind-merge/tree/HEAD/packages/tailwind-merge) | `3.6.0` | `3.7.0` | | [yaml](https://github.com/eemeli/yaml) | `2.9.0` | `2.9.1` | | [@testing-library/dom](https://github.com/testing-library/dom-testing-library) | `10.4.1` | `10.4.2` | | [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) | `26.4.1` | `26.5.1` | | [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react) | `19.2.18` | `19.3.0` | | [@types/react-dom](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react-dom) | `19.2.7` | `19.3.0` | | [eslint-plugin-react-refresh](https://github.com/ArnaudBarre/eslint-plugin-react-refresh) | `0.5.6` | `0.5.7` | | [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint) | `8.69.0` | `8.70.0` | | [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) | `8.2.2` | `8.3.0` | Updates `cronstrue` from 3.24.0 to 3.26.0 - [Release notes](https://github.com/bradymholt/cRonstrue/releases) - [Changelog](https://github.com/bradymholt/cRonstrue/blob/main/CHANGELOG.md) - [Commits](https://github.com/bradymholt/cRonstrue/compare/v3.24.0...v3.26.0) Updates `lucide-react` from 1.41.0 to 1.46.0 - [Release notes](https://github.com/lucide-icons/lucide/releases) - [Commits](https://github.com/lucide-icons/lucide/commits/1.46.0/packages/lucide-react) Updates `motion` from 13.2.0 to 13.3.0 - [Changelog](https://github.com/motiondivision/motion/blob/main/CHANGELOG.md) - [Commits](https://github.com/motiondivision/motion/compare/v13.2.0...v13.3.0) Updates `react` from 19.2.8 to 19.3.0 - [Release notes](https://github.com/react/react/releases) - [Changelog](https://github.com/react/react/blob/main/CHANGELOG.md) - [Commits](https://github.com/react/react/commits/v19.3.0/packages/react) Updates `@types/react` from 19.2.18 to 19.3.0 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react) Updates `react-dom` from 19.2.8 to 19.3.0 - [Release notes](https://github.com/react/react/releases) - [Changelog](https://github.com/react/react/blob/main/CHANGELOG.md) - [Commits](https://github.com/react/react/commits/v19.3.0/packages/react-dom) Updates `@types/react-dom` from 19.2.7 to 19.3.0 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react-dom) Updates `react-is` from 19.2.8 to 19.3.0 - [Release notes](https://github.com/react/react/releases) - [Changelog](https://github.com/react/react/blob/main/CHANGELOG.md) - [Commits](https://github.com/react/react/commits/v19.3.0/packages/react-is) Updates `tailwind-merge` from 3.6.0 to 3.7.0 - [Release notes](https://github.com/dcastil/tailwind-merge/releases) - [Commits](https://github.com/dcastil/tailwind-merge/commits/tailwind-merge@3.7.0/packages/tailwind-merge) Updates `yaml` from 2.9.0 to 2.9.1 - [Release notes](https://github.com/eemeli/yaml/releases) - [Commits](https://github.com/eemeli/yaml/compare/v2.9.0...v2.9.1) Updates `@testing-library/dom` from 10.4.1 to 10.4.2 - [Release notes](https://github.com/testing-library/dom-testing-library/releases) - [Changelog](https://github.com/testing-library/dom-testing-library/blob/main/CHANGELOG.md) - [Commits](https://github.com/testing-library/dom-testing-library/compare/v10.4.1...v10.4.2) Updates `@types/node` from 26.4.1 to 26.5.1 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) Updates `@types/react` from 19.2.18 to 19.3.0 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react) Updates `@types/react-dom` from 19.2.7 to 19.3.0 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react-dom) Updates `eslint-plugin-react-refresh` from 0.5.6 to 0.5.7 - [Release notes](https://github.com/ArnaudBarre/eslint-plugin-react-refresh/releases) - [Changelog](https://github.com/ArnaudBarre/eslint-plugin-react-refresh/blob/main/CHANGELOG.md) - [Commits](https://github.com/ArnaudBarre/eslint-plugin-react-refresh/compare/v0.5.6...v0.5.7) Updates `typescript-eslint` from 8.69.0 to 8.70.0 - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.70.0/packages/typescript-eslint) Updates `vite` from 8.2.2 to 8.3.0 - [Release notes](https://github.com/vitejs/vite/releases) - [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md) - [Commits](https://github.com/vitejs/vite/commits/create-vite@8.3.0/packages/vite) --- updated-dependencies: - dependency-name: cronstrue dependency-version: 3.26.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-npm-frontend - dependency-name: lucide-react dependency-version: 1.46.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-npm-frontend - dependency-name: motion dependency-version: 13.3.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-npm-frontend - dependency-name: react dependency-version: 19.3.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-npm-frontend - dependency-name: "@types/react" dependency-version: 19.3.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: all-npm-frontend - dependency-name: react-dom dependency-version: 19.3.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-npm-frontend - dependency-name: "@types/react-dom" dependency-version: 19.3.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: all-npm-frontend - dependency-name: react-is dependency-version: 19.3.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-npm-frontend - dependency-name: tailwind-merge dependency-version: 3.7.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-npm-frontend - dependency-name: yaml dependency-version: 2.9.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-npm-frontend - dependency-name: "@testing-library/dom" dependency-version: 10.4.2 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: all-npm-frontend - dependency-name: "@types/node" dependency-version: 26.5.1 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: all-npm-frontend - dependency-name: "@types/react" dependency-version: 19.3.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: all-npm-frontend - dependency-name: "@types/react-dom" dependency-version: 19.3.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: all-npm-frontend - dependency-name: eslint-plugin-react-refresh dependency-version: 0.5.7 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: all-npm-frontend - dependency-name: typescript-eslint dependency-version: 8.70.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: all-npm-frontend - dependency-name: vite dependency-version: 8.3.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: all-npm-frontend ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|
|
a82cf5e42d |
fix(ui): prevent modal footer overflow and keep a viewport gutter (#1928)
ModalFooter now lets the contextual hint shrink and wrap long unbroken values, keeps the action group non-shrinking, and may flow to a second row instead of pushing the actions outside the panel. The shared dialog and alert-dialog content primitives derive their width from the viewport minus a small gutter on narrow windows while retaining the max-w size caps; the opt-in mobileFullScreen behavior is unchanged. Adds geometry coverage for the New file / New folder dialogs at 1280px, a phone-width delete-confirm case through the mobile-reachable flow, a one-row desktop assertion, and ModalFooter containment contract tests. |
||
|
|
f40a6c3dfa |
chore(deps): bump the all-actions group across 1 directory with 7 updates (#1936)
Bumps the all-actions group with 7 updates in the / directory: | Package | From | To | | --- | --- | --- | | [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) | `4.3.0` | `4.4.0` | | [docker/build-push-action](https://github.com/docker/build-push-action) | `7.3.0` | `7.4.0` | | [github/codeql-action/init](https://github.com/github/codeql-action) | `4.37.9` | `4.38.0` | | [github/codeql-action/autobuild](https://github.com/github/codeql-action) | `4.37.9` | `4.38.0` | | [github/codeql-action/analyze](https://github.com/github/codeql-action) | `4.37.9` | `4.38.0` | | [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) | `4.3.0` | `4.4.0` | | [github/codeql-action/upload-sarif](https://github.com/github/codeql-action) | `4.37.9` | `4.38.0` | Updates `docker/setup-buildx-action` from 4.3.0 to 4.4.0 - [Release notes](https://github.com/docker/setup-buildx-action/releases) - [Commits](https://github.com/docker/setup-buildx-action/compare/37fe631027851001ddb9b187196cc803df7f5f0e...594f3bf4285d9ea8dc53c9a0c9c4092420091003) Updates `docker/build-push-action` from 7.3.0 to 7.4.0 - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/53b7df96c91f9c12dcc8a07bcb9ccacbed38856a...c3c9e263c25d99ce0380d002d59b67737d91b0dc) Updates `github/codeql-action/init` from 4.37.9 to 4.38.0 - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/cdf488f595d80d6e07e03d4674febd5ab45fa938...b96794f015dfd88f77b49b1c93e0fa7110f94c63) Updates `github/codeql-action/autobuild` from 4.37.9 to 4.38.0 - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/cdf488f595d80d6e07e03d4674febd5ab45fa938...b96794f015dfd88f77b49b1c93e0fa7110f94c63) Updates `github/codeql-action/analyze` from 4.37.9 to 4.38.0 - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/cdf488f595d80d6e07e03d4674febd5ab45fa938...b96794f015dfd88f77b49b1c93e0fa7110f94c63) Updates `docker/setup-qemu-action` from 4.3.0 to 4.4.0 - [Release notes](https://github.com/docker/setup-qemu-action/releases) - [Commits](https://github.com/docker/setup-qemu-action/compare/1f40c72289eff860ee54a304f1438e3cff362e0a...99012661954931238ded8c8b007157a8430204e1) Updates `github/codeql-action/upload-sarif` from 4.37.9 to 4.38.0 - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/cdf488f595d80d6e07e03d4674febd5ab45fa938...b96794f015dfd88f77b49b1c93e0fa7110f94c63) --- updated-dependencies: - dependency-name: docker/setup-buildx-action dependency-version: 4.4.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-actions - dependency-name: docker/build-push-action dependency-version: 7.4.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-actions - dependency-name: github/codeql-action/init dependency-version: 4.38.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-actions - dependency-name: github/codeql-action/autobuild dependency-version: 4.38.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-actions - dependency-name: github/codeql-action/analyze dependency-version: 4.38.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-actions - dependency-name: docker/setup-qemu-action dependency-version: 4.4.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-actions - dependency-name: github/codeql-action/upload-sarif dependency-version: 4.38.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-actions ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|
|
79e988c073 |
chore(deps-dev): bump js-yaml in the all-npm-root group (#1922)
Bumps the all-npm-root group with 1 update: [js-yaml](https://github.com/nodeca/js-yaml). Updates `js-yaml` from 5.4.1 to 5.4.2 - [Changelog](https://github.com/nodeca/js-yaml/blob/master/CHANGELOG.md) - [Commits](https://github.com/nodeca/js-yaml/compare/5.4.1...5.4.2) --- updated-dependencies: - dependency-name: js-yaml dependency-version: 5.4.2 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: all-npm-root ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|
|
4a466e4511 |
chore(deps): bump node from 2d984a1 to ef24c50 (#1921)
Bumps node from `2d984a1` to `ef24c50`. --- updated-dependencies: - dependency-name: node dependency-version: 26-alpine dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|
|
4b2f9aad98 |
fix(resources): zero reclaimable images when held state is unknown (#1912)
When a held-image lookup fails, skip unique-byte subtraction and claim 0 reclaimable image bytes so shared layers cannot remain in the figure. Prune still treats every image as held on the same failure. |
||
|
|
0a6d0bf7fa |
fix(nodes): reject whitespace-only names on create (#1911)
POST /api/nodes treated a whitespace-only name as valid because the check was truthiness-only. PUT already rejected blank names after trim when the name changed. Align create with update: trim is used only for the blank check, the stored name is still written untrimmed, and PUT still allows saving a legacy whitespace-only name unchanged. |
||
|
|
ab6e4940e7 |
feat(ui): add resizable sidebar and Anatomy pane (#1902)
* feat(db): per-user preference store with versioned revision contract Add the user_preferences table (per user and domain, with a strictly advancing integer revision and schema_version) plus the atomic persistence methods behind it: - getUserPreferenceDomain returns a transport envelope with corrupt-row reporting (the revision stays intact so clients can repair) - mutateUserPreferenceDomain applies a document mutation inside a single transaction under an expectedRevision or create-if-absent precondition; two competing writers cannot both succeed - resetUserPreferenceDomain writes a tombstone (schema_version 0) under the same precondition contract, so a post-reset edit can conditionally un-tombstone while a stale pre-reset edit conflicts - deleteUserPreferenceDomains removes every row on account deletion (foreign keys are declarative only in this codebase, so deleteUser runs the cleanup explicitly in its transaction) * feat(preferences): per-user preference API with conditional writes Add the /api/user-preferences router: collection GET, per-domain PUT and DELETE (reset to tombstone), and a migrate endpoint for the create-if-absent first-run race. - Every operation is identity-guarded: the x-sencho-pref-user header must match the authenticated user before any persistence access, so a stale tab can never read or write another account's rows. A mismatch is a 409 IDENTITY_CHANGED with no current envelope, kept distinct from the revision-conflict 409 CONFLICT which carries one. - Writes are conditional (expectedRevision or absent: true); there is no unguarded write path. Stale preconditions return the fresh envelope so clients reconcile instead of retrying blind. - Documents are validated server-side with strict Zod schemas mirroring the frontend field guards; legacy navigation mode values are rejected - Machine identities (node proxy, pilot tunnel) are rejected and API tokens lack the required scope - The route is hub-only: a remote x-node-id targeting it gets an explicit 403, since account-owned state lives on the hub - Audit summaries cover migrate, update, reset, and failed attempts * feat(preferences): parity script pinning server enums to frontend registries The server-side enum tables duplicate the frontend's field guards, and nothing in the TypeScript compilation boundary links the two (backend and frontend compile separately). Add a standalone check script that extracts both value sets from source text and fails on drift, wired as the pretest step for backend and frontend so a mismatch surfaces in the ordinary test run instead of as a 400 at write time. * feat(preferences): frontend preference domains, sync bus, and identity guards Introduce the client half of server-backed per-user preferences: the existing localStorage hooks remain the owners of state and DOM application; a thin sync layer carries their documents to and from the per-user preference API. - preferenceEvents: the leaf module with write notifications (dirty field attribution), the identity-generation counter, the eligibility-readiness store with ownership-captured publications, and the unsaved-episode failure surface - preferencesDocuments: build and per-field sanitize of appearance and navigation documents, tombstone-aware hydration, and the owner marker that wipes cached values when another account claims the browser - syncBus: the single write choke point. Per-domain serialization, same-kind coalescing, reset-cancels-prior-puts chronology, debounced trailing writes, conditional PUTs with 409 reconciliation (reset conflicts re-queue the DELETE once on the adopted revision), keepalive flush on page hide, and pump-level identity guards that drop operations captured by a superseded account or generation - useUserPreferencesSync: the always-mounted sync owner. Hydrates on identity resolution, reconciles late GETs against dirty fields (server wins untouched fields), adopts remote resets by observable revision, repairs corrupt rows with a conditional PUT, and surfaces failures as a single unsaved episode per domain - AuthContext: sequenced identity transitions and a cross-tab storage signal so a login or logout in one tab re-checks auth in the others - the localStorage hooks gain apply/current exports for the hydration path; user-facing setters notify the bus, hydration and derived seeding paths deliberately do not * feat(preferences): reset actions, account-scope copy, and frontend tests Wire the per-domain reset actions into the settings surface: the appearance section footer and navigation section each offer a complete-domain reset, and the recovery section's interface reset runs both domains through the shared handler with a reload only after both tombstones settle successfully. - Settings registry: appearance scope moves from browser to account, with copy that states where preferences now live - ThemeQuickSwitch: the save-location line reflects account syncing - Tests: dirty-field merge against a late hydration GET, identity guards (cache ownership, queue invalidation on account switch, loading-state no-op), corrupt-row repair through the hook path, remote-reset precedence, reset settle/failure handling, registry scope assertions, and the eligibility readiness bridge * test(e2e): cross-browser preference convergence suite Add the end-to-end spec covering the promise the feature makes: two browser contexts on the same account converge (appearance fields, density, navigation mode and pin order), a reset in one context reaches the other on reload, an account switch renders the new account's state rather than the previous account's cache, and a pending write captured before the switch is rejected server-side without touching either account's rows. The existing navigation, motion, and effects suites seed a known server-side preference baseline per suite account so the new hydration layer cannot overwrite their localStorage-only setups. * docs: appearance and navigation preferences are account-scoped Rewrite the user-facing pages that described browser-only personalization: the appearance feature page (including the quick switcher, troubleshooting entries, and screenshot description), the settings reference scope legend, and the multi-node and overview pages that stated preferences stay per browser. The recovery page's emergency-reset entry now explains that clearing site data only drops the local cache, not the saved preferences. * test(preferences): mint API tokens through the shared test helper Replace the duplicated token-minting code with the existing createTestApiToken helper so the file leans on the repo-standard scaffolding and CodeQL stops flagging the inline sha256 hashing as new alerts on this diff. * feat(preferences): sidebar mode and width in the appearance document Extend the appearance preference domain with sidebarMode (fixed or resizable) and sidebarWidth (224 to 440 px, default 256), the persisted contract for the upcoming desktop sidebar resize feature. Frontend: a use-sidebar-layout hook modeled on the density hook (guarded localStorage cache, settings-changed broadcast, per-field dirty-field notification), the new fields in the appearance document builder, hydration with default fallbacks for older documents, both keys in the identity-clear cache key list, and a targeted sidebar-layout reset that puts defaults without tombstoning the domain. The theme cache document excludes the new fields so pre-paint cache rewrites stay consistent. Backend: the strict appearance schema gains both fields with defaults, so an older writer's 16-field document parses and is stored normalized instead of being rejected; out-of-bounds, fractional, and unknown mode values still fail validation. The preference parity script now compares the mode enum across backend and frontend. Tests: backend normalization (PUT and migrate) plus boundary rejection cases, and a frontend hook suite covering sanitization, cached reads, write-path attribution, cross-tab sync, and cache key registration. * feat(shell): resizable stacks sidebar Add a desktop resize boundary for the stacks sidebar. When the sidebar mode preference is resizable, the shell wraps the sidebar in a width- owned pane with a draggable separator: pointer drag updates the pane live and persists one sanitized width on release; pointercancel, lost pointer capture, and unmount clean up without committing. The separator is keyboard accessible (arrows, Home, End) and exposes role=separator ARIA values that track the effective bounds. The effective width clamps to the viewport through a ResizeObserver on the shell flex row (sidebar plus workspace), reserving a minimum workspace width. Narrowing the window shrinks the pane live without rewriting the stored preference; widening restores the preferred width. Fixed mode (the default) renders the original shell DOM unchanged, and mobile is untouched. * fix(shell): harden sidebar resize drag teardown The resize separator now ignores a second pointerdown while a drag is in progress, so an extra touch or mouse button cannot steal or corrupt an active drag. The commit on pointerup releases the drag state in a finally block, so teardown (body cursor, user-select, capture release) always runs even if a consumer's commit handler throws. The drag teardown helper drops its unused commit flag: cancellation paths never commit, and the single pointerup commit now owns that decision explicitly. Covered by three new pane tests: keyboard End clamping to the viewport-limited max, sub-minimum drags clamping to the minimum without a sub-minimum commit, and interleaved second-pointer pointerdowns being ignored. * feat(settings): sidebar layout preference Add a Sidebar layout section to Settings > Appearance with a Fixed/Resizable mode control, a width slider (224 to 440 px, step 4), and a targeted reset that restores the defaults without touching other appearance values. The sidebar layout hook now broadcasts the settings-changed event when one of its setter writes actually changes localStorage, so every mounted instance (the shell separator, the Settings slider, other tabs) converges on the commit instead of holding a stale draft. Settings search keywords gain sidebar/resize/layout aliases, and the preference sync tests cover the targeted reset path: local defaults apply immediately, one bus op carries both dirty fields, and the write degrades to a migrate POST when no preference row exists yet. * fix(shell): review cleanup for the resizable sidebar Restore the Fixed-mode main column class exactly as before (min-w-0 is now applied only in Resizable mode, so the desktop default DOM stays byte-identical), drop the unused committed drag flag, remove a stray blank line and the redundant width-commit wrapper, and align the separator keyboard step on the 8 px used by the file explorer pane. * test(e2e): sidebar resize journeys Cover the resizable stacks sidebar end to end: Fixed default with no separator, the Settings opt-in writing one preference PUT, drag and keyboard resize with release as the persistence boundary, viewport clamping that never persists the clamped width, mode-toggle retention, a targeted reset that leaves other appearance values untouched, per-account scoping, and an unchanged phone layout. The shared preference GET helper now retries briefly on rate-limit responses so a burst of suite traffic cannot fail an assertion read. * docs(preferences): sidebar layout docs and E2E polling hardening User-facing docs for the sidebar layout preference: a Display bullet and new Sidebar section in the Appearance feature page (with screenshot), a Display row in the settings reference, and an appearance row update on the multi-node page stating that sidebar layout is account-scoped. E2E: every server-state assertion now polls (expectStoredAppearance helper over GET user-preferences) instead of relying on a fixed sleep, the dead flushPreferenceWrite/DEBOUNCE_MS pair is removed, the targeted-reset seed uses non-default theme and density so the "untouched" assertion is meaningful, and serial hand-off points gained storage boundary proofs. Helper retry comment now states the 429 retry window explicitly. * feat: add resizable Anatomy pane * fix: expand Anatomy resize range * test: build storage events without the two-arg constructor Static analysis models StorageEvent as one-argument only. The tests still need key and newValue, so those fields are defined on a generic storage Event. * ci: give Playwright E2E thirty minutes on PRs The pane-layout journeys pushed the chromium suite past the old twenty-minute job limit, so the run was cancelled mid-suite. |
||
|
|
e7b457f456 |
feat(preferences): server-backed per-user appearance and navigation (#1901)
* feat(db): per-user preference store with versioned revision contract Add the user_preferences table (per user and domain, with a strictly advancing integer revision and schema_version) plus the atomic persistence methods behind it: - getUserPreferenceDomain returns a transport envelope with corrupt-row reporting (the revision stays intact so clients can repair) - mutateUserPreferenceDomain applies a document mutation inside a single transaction under an expectedRevision or create-if-absent precondition; two competing writers cannot both succeed - resetUserPreferenceDomain writes a tombstone (schema_version 0) under the same precondition contract, so a post-reset edit can conditionally un-tombstone while a stale pre-reset edit conflicts - deleteUserPreferenceDomains removes every row on account deletion (foreign keys are declarative only in this codebase, so deleteUser runs the cleanup explicitly in its transaction) * feat(preferences): per-user preference API with conditional writes Add the /api/user-preferences router: collection GET, per-domain PUT and DELETE (reset to tombstone), and a migrate endpoint for the create-if-absent first-run race. - Every operation is identity-guarded: the x-sencho-pref-user header must match the authenticated user before any persistence access, so a stale tab can never read or write another account's rows. A mismatch is a 409 IDENTITY_CHANGED with no current envelope, kept distinct from the revision-conflict 409 CONFLICT which carries one. - Writes are conditional (expectedRevision or absent: true); there is no unguarded write path. Stale preconditions return the fresh envelope so clients reconcile instead of retrying blind. - Documents are validated server-side with strict Zod schemas mirroring the frontend field guards; legacy navigation mode values are rejected - Machine identities (node proxy, pilot tunnel) are rejected and API tokens lack the required scope - The route is hub-only: a remote x-node-id targeting it gets an explicit 403, since account-owned state lives on the hub - Audit summaries cover migrate, update, reset, and failed attempts * feat(preferences): parity script pinning server enums to frontend registries The server-side enum tables duplicate the frontend's field guards, and nothing in the TypeScript compilation boundary links the two (backend and frontend compile separately). Add a standalone check script that extracts both value sets from source text and fails on drift, wired as the pretest step for backend and frontend so a mismatch surfaces in the ordinary test run instead of as a 400 at write time. * feat(preferences): frontend preference domains, sync bus, and identity guards Introduce the client half of server-backed per-user preferences: the existing localStorage hooks remain the owners of state and DOM application; a thin sync layer carries their documents to and from the per-user preference API. - preferenceEvents: the leaf module with write notifications (dirty field attribution), the identity-generation counter, the eligibility-readiness store with ownership-captured publications, and the unsaved-episode failure surface - preferencesDocuments: build and per-field sanitize of appearance and navigation documents, tombstone-aware hydration, and the owner marker that wipes cached values when another account claims the browser - syncBus: the single write choke point. Per-domain serialization, same-kind coalescing, reset-cancels-prior-puts chronology, debounced trailing writes, conditional PUTs with 409 reconciliation (reset conflicts re-queue the DELETE once on the adopted revision), keepalive flush on page hide, and pump-level identity guards that drop operations captured by a superseded account or generation - useUserPreferencesSync: the always-mounted sync owner. Hydrates on identity resolution, reconciles late GETs against dirty fields (server wins untouched fields), adopts remote resets by observable revision, repairs corrupt rows with a conditional PUT, and surfaces failures as a single unsaved episode per domain - AuthContext: sequenced identity transitions and a cross-tab storage signal so a login or logout in one tab re-checks auth in the others - the localStorage hooks gain apply/current exports for the hydration path; user-facing setters notify the bus, hydration and derived seeding paths deliberately do not * feat(preferences): reset actions, account-scope copy, and frontend tests Wire the per-domain reset actions into the settings surface: the appearance section footer and navigation section each offer a complete-domain reset, and the recovery section's interface reset runs both domains through the shared handler with a reload only after both tombstones settle successfully. - Settings registry: appearance scope moves from browser to account, with copy that states where preferences now live - ThemeQuickSwitch: the save-location line reflects account syncing - Tests: dirty-field merge against a late hydration GET, identity guards (cache ownership, queue invalidation on account switch, loading-state no-op), corrupt-row repair through the hook path, remote-reset precedence, reset settle/failure handling, registry scope assertions, and the eligibility readiness bridge * test(e2e): cross-browser preference convergence suite Add the end-to-end spec covering the promise the feature makes: two browser contexts on the same account converge (appearance fields, density, navigation mode and pin order), a reset in one context reaches the other on reload, an account switch renders the new account's state rather than the previous account's cache, and a pending write captured before the switch is rejected server-side without touching either account's rows. The existing navigation, motion, and effects suites seed a known server-side preference baseline per suite account so the new hydration layer cannot overwrite their localStorage-only setups. * docs: appearance and navigation preferences are account-scoped Rewrite the user-facing pages that described browser-only personalization: the appearance feature page (including the quick switcher, troubleshooting entries, and screenshot description), the settings reference scope legend, and the multi-node and overview pages that stated preferences stay per browser. The recovery page's emergency-reset entry now explains that clearing site data only drops the local cache, not the saved preferences. * test(preferences): mint API tokens through the shared test helper Replace the duplicated token-minting code with the existing createTestApiToken helper so the file leans on the repo-standard scaffolding and CodeQL stops flagging the inline sha256 hashing as new alerts on this diff. |
||
|
|
7569f1d763 |
fix: calm shared modal open/close motion (#1931)
The shared modal opened and closed with a 3D flip (perspective + rotateX/rotateY + scale 0.8) driven by a spring, which made the modal a prominent visual event rather than subordinate to its content. Replace it with calm motion: a short ease-out fade with a small rise (opacity + translateY 6px + scale 0.98), no rotation, no perspective, and no spring rebound. Drop the now-dead flip plumbing (the from prop, the DialogFlipDirection type and export, and the rotation helpers). Reduced-motion handling is preserved: the existing instant path still short-circuits the tween when the app's reduced-motion setting is on. The overlay scrim motion is unchanged. |
||
|
|
ea905b758b |
feat(gitops): bind Blueprint content to a live Git source (#1926)
* feat(db): bind Blueprint content origin to a GitOps application Pair Git-managed Blueprints one-to-one with an application at the schema layer, and retain the original Direct stack identity so conversion can reuse credentials without minting a second live application. * feat(gitops): add guarded Blueprint binding transitions Reconfigure a live Direct application in place to Blueprint mode through a replay-safe store update, keeping identity and generation lineage stable while recording the mode change in history. * feat(gitops): convert Blueprints onto a live GitOps application Move an existing Direct application in place, freeze Inline YAML as a detach snapshot, and refuse compose edits and deletes while content is Git-managed. Previews expose non-secret source fields and rollback limitations only. * fix(blueprints): refuse Git-managed deploy, apply, and delete Skip reconciler and service apply paths, return 409 before withdraw or approval, and mint a blocked rollout candidate so a converted application stays visible as not executable. * feat(gitops): keep converted applications on the source controller Widen poll and retry scans to converted Blueprint sources, route accepted generations from the live target mode, and refuse Git-tab mutations while a Blueprint still holds the source stack. * feat(api): add Blueprint GitOps content-binding endpoints Expose convert, detach, and adopt so a Blueprint can bind a live Direct application without leaking retained source-stack identity. * feat(ui): add Git-managed Blueprint editor and binding dialogs Operators can convert, adopt, and detach Blueprint content onto a live Git source. The editor and Git tab stay read-only while rollout is blocked. * chore: document Git-managed Blueprint content binding Operator docs and What's New cover convert, detach, and the blocked rollout state. Marker parse keeps optional binding evidence, and Git-managed dispatch uses the operator-facing blocked reason. * test: type BlueprintEditor submit mocks without unused params * fix(gitops): close Blueprint binding audit gaps before QA Expose Retire routes and UI, return 409 on Git-managed blueprint PUT edits, and render binding preview markers and node actions in convert, adopt, detach, and retire dialogs. * fix(gitops): make claimed sheet, adopt, and retire paths production-safe Project blueprint-claimed sources on git-source GET. Adopt tombstones inline apps like convert. Retire restores the original stack identity and clears evidence. * chore(gitops): close non-blocking binding follow-ups Refresh stale projection comments, document Retire, include converted sources in polling settings, and remove unused blueprintModePromoted. * docs: mention Retire in Blueprint intro copy Keep the overview consistent with Detach-or-Retire exit paths. |
||
|
|
e41722db56 |
fix(gitops): return reconciliation results from manual pulls (#1927)
Preserve the pull payload while exposing its canonical reconciliation result. Return an explicit unknown result when the caller's settlement fails, including coalesced followers whose leader settled successfully. |
||
|
|
a375861716 |
feat(dashboard): Stack Health All nodes inventory (#1918)
* feat(dashboard): add Stack Health All nodes inventory Keep This node as the default, and add an opt-in All nodes view that fans out statuses, metrics, and GitOps to non-active nodes with independent outcomes, State/Networks columns, Show all expansion, and node-qualified editor navigation. * fix(dashboard): restore Stack Health remote navigation and remember scope All-nodes row clicks now stash-then-switch so the editor opens on the owning node. Networks no longer overlap Port, and This node / All nodes persists across views and node switches. |
||
|
|
71595cb1ce |
feat(gitops): source controller follow-on (#1920)
* feat(gitops): revalidate candidates append-only and persist a settled outbox Record the candidate digest and source-policy evidence at insert. Auto-accept remints a new generation when evidence changed, refuses missing hashes and fingerprint drift, and never updates a generation row. Settled history writes a versioned outbox so notification fanout survives a crash, with unique dedupe and unknown versions left undrained. * feat(gitops): convert Git source to a SystemSheet and advertise controller controls Move the Git source panel to the canonical right-side sheet, add suspend, resume, retry, and node polling, and register gitops-source-controller on both sides so the controls stay hidden until the node advertises them. * fix(gitops): keep candidate compose reads inside the generation directory Resolve each compose path against the candidate dir and refuse when it escapes, both when recording the content digest and when revalidating before auto-accept. * fix(gitops): close audit gaps on digest reuse, suspend pull, and polling load Pre-upgrade candidates without a content digest now mint anew instead of reusing forever, Pull now hides while suspended with matching copy, and Git polling skips GET for users without node:manage. |
||
|
|
ce8b454600 |
feat(registry): exact-ref private-registry delivery to remote nodes (#1905)
Discover the exact image pull references a remote deploy needs, probe which registries gate them behind credentials, and attach short-lived registry credentials to the proxied request so remote nodes can pull private images. - Canonical pull-reference module: parse and normalize exact refs (library/ prefixes, :latest defaults, Docker Hub alias folding), dedupe and sort, with a 200-ref and 64 KiB list limit surfaced as 413 - Anonymous manifest probe module over the safe transport: classify a registry as public, challenged, or inconclusive without ever contacting unsafe hosts - Refusal matrix on the proxy hop: 409 credential-unavailable and transport-not-confidential, 413 envelope and body limits, 499 aborts, all with machine-readable codes - Validate the target's discover response before trusting it: shape check plus a canonical round-trip of the pull-ref list, and a response size cap - Contract version on discovery: the request always names it, the target tolerates requests without one and rejects unsupported versions with 400 - Narrowed proxy delivery result type: every refusal carries status, message, and code, with no silent fallbacks at the proxy layer - Hub-side consumers updated to the shared prepare path (fleet, scheduler, mesh, blueprints, webhooks), and docs plus the frontend capability list refreshed |
||
|
|
e8da1fd64b |
fix(fleet): bound unreachable proxy overview probes (#1919)
* fix(fleet): bound unreachable proxy overview probes A configured-but-dead Distributed API Proxy host held GET /api/fleet/overview for about 10s, so Fleet, Heartbeat, and Mobile stayed on first-load states. Cap the three overview probes at 3s and map outer fan-out rejections through the existing offline overview shape. * test(fleet): compare mixed-fleet mock fetches by URL origin Prefix matching a host string can treat a lookalike URL as the healthy node. Dispatch the mixed-fleet mock by parsed origin instead. |
||
|
|
d4ef263d6e |
feat(gitops): stage-aware retry and resume re-evaluation (#1917)
* feat(gitops): retry resumes at the failed stage An operator retry previously always re-issued a fetch, even when the source had already accepted a generation whose dispatch never landed. retry() now reads the live pointer evidence (accepted generation, active direct target, applied pointer not yet caught up) and routes the request to the stage the evidence actually stopped at: a persisted source-stage failure or nothing pending goes to the fetch arm, an undelivered accepted generation goes straight through the shared dispatch boundary, and suspended/in-flight/recovering rows defer to the projection instead of starting work beside it. Blocked dispatch refusals come back as blocked outcomes with per-refusal advice; a refused dispatch reserves nothing. * feat(gitops): resume triggers immediate source re-evaluation Clearing a suspension previously left the source idle until its next poll or retry cursor. The resume route now fires SourceController.evaluateNow() fire-and-forget after the service returns: the woken source is re-evaluated immediately through the same eligibility guards the due-scan applies (manual, suspended, missing, or in-flight rows are skipped, and a tick that already owns the application is never queued behind itself). The response still reports that suspension was cleared, not the outcome of a reconcile that has not finished yet. evaluateNow absorbs evaluation failures by design and logs them; the route's catch covers only a store error before any evaluation could start. * test(gitops): cover suspension semantics across resume and dispatch Pins the three coexistence rules the stage-aware retry and resume wake rely on: a rollout pause survives the whole suspend/resume cycle on both application and target rows (suspension is a decision about source work, not a rollout pause); an interrupted apply's terminal still lands after suspension (suspension gates new work, never the accounting of work that already committed); and in-flight deploy transitions, tracked on the target row alone, stay admissible while the source is suspended. * test(e2e): suspend and resume a Git source over the API Drives the full journey against a local git server: a configured automatic source pulls, suspension stops reconciliation (a pull while suspended is refused outright), and resume is re-evaluated within seconds rather than at the next scheduled cursor. Polling is parked at the slowest floor the settings allow and restored afterwards. |
||
|
|
30e39368ab |
fix: preserve exact guardrail message for Save & Deploy and Start (#1898)
* feat: preserve exact guardrail message for Save & Deploy and Start When 'Block deploy on missing required env vars' is enabled, the exact guardrail message now reaches Save & Deploy and Start (not just Update). Previously the external-networks preflight replaced the message with a generic networking error. The fix selects the guardrail-aware message inside the existing render pipeline without duplicating the render. Changes: - Backend: isGuardrailEnabled() plus guardrail-aware message selection in renderModel(), with exact singular/plural grammar matching the ComposeService guard method. - Frontend: parseMissingExternalNetworksPayload validates non-empty renderError; missingExternalBlocksDeploy prefers it with generic fallback; MissingExternalNetworksPayload extended with optional field. - Tests: backend regression for plural grammar; frontend regression for guardrail-on/off, older-node fallback, empty-string fallback, and no deploy-POST assertions. - Documentation: environment-guardrails tutorial updated. * fix: log guardrail-setting read failures and fix guardrail test isolation isGuardrailEnabled() dropped its nodeId on read failure instead of logging it, unlike the sibling isAutoCreateEnabled() next to it. The new guardrail-message tests never enabled the guided external-network preflight flag they were meant to exercise, and never reset the shared apiFetch mock, so their assertions were checked against leftover calls from unrelated earlier tests in the file. |
||
|
|
8d9522c54f |
fix(proxy): resolve node id consistently before deciding to bypass json parsing (#1880)
conditionalJsonParser decided whether to preserve the raw request stream for remote-node proxying by checking only the x-node-id header. nodeContextMiddleware, the actual authoritative node resolver, also accepts a ?nodeId= query param and a default-node fallback. When a remote target was conveyed without the header, the body was fully parsed and drained, and the proxy then hit a write-after-end race trying to pipe the already-ended stream, hanging the request indefinitely with no error. Both middleware now resolve the target node through a single shared function (helpers/resolveNodeId.ts), so the two can no longer disagree on which node a request targets. |
||
|
|
3f917059ec |
feat(gitops): dispatch accepted generations through the shared pipeline (#1913)
* refactor(gitops): extract the shared completion pipeline out of applyLockedBody
Recovery capture, promotion, the four last-plan outcomes, the applied
mark, the acceptance transition, managed-path conflict resolution, cache
invalidation, the Compose deploy, recovery handoff, and the health gate
now run as one private completeGitApply method. applyLockedBody keeps
pending decode, plan recompute, and fingerprint validation, which are
about the pending pull and are not shared with dispatch.
Behavior-preserving: the moved body is verbatim except for parameter
plumbing, and the full git-source-service suite (241 tests) passes
unchanged. Three new parity tests pin the seams dispatch will reuse:
promotion-failure last-plan outcomes (rolled_back vs failed) with
recovery abandonment, and the health gate binding to the deployed
generation id.
* feat(gitops): dispatch accepted generations through the shared pipeline
Rewrite the Direct branch of dispatchAcceptedGeneration so an accepted
generation is promoted from its own staged candidate instead of
re-entering reconcile()/apply(). The old path validated a pending pull
that no longer exists after acceptance: applyStarted refuses
post-acceptance generations, and the sha/pending checks belonged to the
fetch, not the dispatch.
The new boundary holds the shared git_apply stack lock, reserves one
durable reconcile attempt inside it, revalidates the live target (mode,
suspension, acceptance pointer, target candidate pointer, acceptance
evidence) plus a change plan recomputed from the candidate directory via
discoverFromClone, and refuses promotion on any drift while leaving
source acceptance intact. Only then does the shared completion pipeline
run, with no gitops acceptance of its own; targetApplied() binds the
target after promotion commits. Every refusal settles the attempt and
returns blocked without throwing, so contention and revalidation
failures never leave an unsettled reservation.
Also folds in two pending fixes from the pipeline extraction: the
completion-order paragraph in the completeGitApply doc comment, and
passing the raw actor at the applyLockedBody delegate so the
recovery-capture fallback stays reachable.
* fix(gitops): settle dispatch refusals for every throw between reservation and binding
dispatchAcceptedGeneration reserved a reconcile attempt and then revalidated
the live target outside its try block, so a throw from a store read, a
candidate file, or computeChangePlan's authored-invocation read escaped
runExclusive with the attempt reserved and unsettled. The collision return of
allocateReconcileAttempt was also treated as success, risking a
double-promotion against another attempt's reservation.
Move revalidation, the completion pipeline, and the target binding into one
try. Classify refusals by what the live Compose files look like when the
attempt settles: a plain blocked refusal before the promotion commits, and
recovery_required with view_target_results once the files may already carry
the incoming generation. The pipeline reports that boundary through a
required by-reference out-param that is set when promoteGeneration commits or
when its automatic restore also failed (phase recovery_required); a clean
rollback (phase restored) keeps the truthful plain blocked, and a
GitSourceError rethrow, which promoteGeneration never produces today, sets
the flag before propagating rather than trusting its message to mean
pre-mutation. The binding-write failure path, which returns false instead of
throwing after the promotion committed, now settles recovery_required like
the throw paths at the same boundary. Every settled reason is scrubbed of
credential-shaped text at the one choke point all refusals share, and the
dispatch catch logs the full stack before classifying, because the settled
row keeps only the reason.
A reservation-id collision blocks without settling: nothing was reserved, so
the pre-existing attempt stays owned by whoever recorded it.
Tests pin the restored-versus-recovery_required split, the binding-write
failure, the reservation collision, the dispatch-throw logging, and the crash
matrix: an in-process settle failure leaves the dispatch attempt unsettled,
and startup recovery settles it exactly once under the original operation id
without re-promoting. An auto-apply webhook delivery with auto-deploy off is
pinned to converge with nothing left unsettled.
* feat(gitops): surface the compose deploy operation id on apply results
A completed Git deploy already mints a canonical GitOps operation id in
ComposeService.beginGitOpsDeploy, and the deploy's own transition rows carry
it. The caller of a git apply or update could not see that id, so matching an
apply result against the deploy's recorded operations meant re-deriving the
identity from the stack and hoping no other deploy had interleaved.
beginGitOpsDeploy now returns { generationId, gitopsOperationId, bound,
failed }, and deployStack/updateStack carry the id back on the result as a
three-state field: a string means a tracked deploy succeeded, null means the
deploy succeeded untracked (no GitOps identity, or the tracking write failed),
and absence means the result did not come from a completed deploy. The
GitSourceService apply pipeline forwards it on GitApplyResult and names it in
the applied-and-deployed log line, so operator-visible evidence for one apply
references the exact operation the deploy wrote.
* feat(gitops): route controller acceptance through the dispatch boundary
The source controller's automatic apply went through reconcile() with an
apply intent, the fused fetch+apply path a manual apply runs: the controller
re-fetched, re-validated, and re-planned work the acceptance pass had already
decided on. That duplicated the pipeline and let the controller act on a
newly fetched candidate instead of the generation it had just accepted.
After accepting a candidate (authority configured_policy), the controller now
builds the accepted-generation handoff and calls
GitSourceService.dispatchAcceptedGeneration directly: the same shared
boundary the manual dispatch route uses, which revalidates the live target
under the stack lock and promotes the accepted generation's own staged
candidate, reading the deploy choice from the source row under the lock.
Refusals come back as blocked outcomes and are logged with their reason;
anything that still throws can only be pre-reservation, so the error log
states that the generation remains accepted and must be dispatched manually.
The policy suite's dispatch tests pin the handoff shape (target mode, node,
trigger, system actor) and the blocked/throw logging behavior.
* fix(gitops): bind the Direct target before the deploy branch
The extracted pipeline bound the Direct target after the whole completion
sequence, so an auto-dispatched deploy opened its GitOps operation while
beginGitOpsDeploy still read the previous generation's applied pointer
(or none at all on the first dispatch). The bind now runs inside
completeGitApply via a postPromote hook, at the same position manual
apply's acceptance transition occupies: after the promotion commits,
before the deploy branch. A rejected bind settles the reserved attempt as
recovery_required, matching the post-commit throw paths.
Also in this pass:
- A revalidation refusal whose own text says "try again" (an IO blip on
the candidate or staged sync env read) now carries a transient flag and
settles with a retry next action instead of conflict resolution, so the
durable row's advice matches the real remedy.
- The policy gate inside the extracted pipeline falls back to the
'git-source' actor the manual-apply call site used before extraction,
restoring byte parity with the pre-refactor behavior.
* test(gitops): pin the dispatch settle classification branches
- Revalidation guard table: each reachable refusal (suspended app, moved
acceptance pointer, missing evidence, tombstoned target, moved target
candidate) pins its blocked reason and the exact settled row shape.
- Credential scrub at the refusal choke point: a secret-shaped refusal is
persisted scrubbed and read back from the durable row.
- Fail-dangerous arm: an unknown-provenance GitSourceError refusal past
the reservation settles recovery_required with no target bind.
- Reservation outage: an already-reserved operation id blocks, reserves
and settles nothing, and leaves the pre-existing attempt alone.
- Bind-before-deploy order: the promote, bind, and deploy side effects
replay in that sequence, so a deployed dispatch records against the
generation it just applied.
- Untracked deploy: an applied line with no deploy operation id keeps no
stale "(deploy op" fragment.
- Transient revalidation refusal settles with a retry next action.
- Settle-crash recovery pin: startup recovery derives the row's outcome
(no_source_change) under the original operation id without
re-promoting.
- Controller logging arms: a blocked dispatch logs the refusal, a
pre-reservation throw logs the manual-dispatch remedy; acceptance
stays put in both.
* fix(gitops): harden dispatch logging, bind gating, startup recovery, and deploy correlation
Closes the review round on the dispatch boundary:
- Every dispatch-path error log value is credential-scrubbed before it
reaches the server log. scrubCredentials now also covers URL userinfo,
Bearer/Basic authorization headers, and JWT-shaped triples, and the
controller's pre-reservation failure log redacts the full stack.
- A rejected Direct target bind stops the pipeline before the policy gate
is evaluated and before Compose runs: the postPromote hook now gates on
its boolean result instead of only recording a caller-side flag.
- Startup recovery distinguishes dispatch crash stages. Dispatch
reservations carry a generation marker; recovery reads bind and
promotion evidence scoped to the exact attempt and settles unknown,
recovery_required, or the derived result accordingly. Failed
post-commit settlements mirror their reason into the activity feed so a
lost DB write does not erase the operator's evidence.
- Settled attempts carry the canonical deploy-operation id of the tracked
deploy they correspond to (success and failure arms), stamped onto
Compose errors at both mutation seams and round-tripped through a single
payload validator that drops unknown or non-string fields.
- The dispatch-time change-plan fingerprint check fails closed: a
generation with no recorded fingerprint is refused instead of treated
as "no drift".
* test(gitops): regression coverage for the dispatch-boundary review round
- Reservation, promotion, and controller log surfaces are asserted free
of credential-shaped secrets (URL userinfo, Bearer, Basic, JWT) with
the scrub marker present instead.
- The bind-rejection test now asserts the activity-feed mirror lands when
the settle write itself fails, and the recovery tests pin the
fingerprint disjunct and the unmarked-reservation parity.
- Compose stamping is pinned on all three rethrow arms: plain deploy, the
atomic rollback wrapper, and the update seam.
- The settled-row correlation is asserted present on tracked deploy arms
and absent on an untracked deploy, plus the payload validator round
trip and non-string drop, and stage-row lookups are scoped to the exact
operation, not the application.
* fix(gitops): scope promotion evidence and deploy intent to the dispatch operation
Recovery of an interrupted dispatch proved the promotion from
source-level rows (applied commit mark, last plan fingerprint) that are
written after the filesystem commit, so a crash inside that window made
recovery claim nothing was applied while the files had changed, and a
new generation sharing an earlier one's commit or fingerprint inherited
promotion evidence it never earned. Dispatch now writes an
operation-scoped promotion_committed witness row at the filesystem
commit boundary, before any bookkeeping, and recovery reads only that
row.
Post-bind recovery also could not tell a lost settlement from a deploy
that never began or failed, and the settled row lost the deploy's
canonical id. Dispatch journals a deploy_dispatched intent naming a
deploy operation id it mints, reads the stored row back, and threads
the id it finds there into the deploy invocation, so the id Compose
records its transitions under is always the id recovery will look up.
The two witness payloads are typed, and a missing or malformed deploy
operation id in an intent row never yields a guessed id. The
pre-promotion and no-deploy-record recovery reasons now state only what
the rows can prove, and recovery reconstructs the deploy outcome from
those exact rows instead of source-level state. Witness and intent
writes are best-effort: their absence only ever makes recovery less
confident, never more.
* test(gitops): pin witness-scoped recovery and deploy reconstruction crash windows
Regression coverage for the dispatch-boundary review round two:
a crash between the filesystem promotion and the source bookkeeping now
settles recovery_required from the witness alone with every source-level
mark erased; a new accepted generation sharing the applied commit and
plan fingerprint can no longer inherit the previous promotion's evidence
and settles as nothing-applied with a retry; a journaled deploy that
never started settles as an interrupted deploy with a retry and no
claimed id; a completed or failed tracked deploy settles with the exact
deploy id threaded through the intent row, without re-promoting or
redeploying, and repeated recovery keeps one settlement per attempt.
Round three pins the rest of the reconstruction arms and the threading
itself: the real Compose chain honors a supplied deploy operation id and
records its started and bound transitions under it; a deploy that
opened without a terminal record settles as unproven, naming its id; an
unbound terminal record settles the same as a failed one; and a fully
completed pipeline settles as exactly no_source_change with no next
action.
* fix(gitops): journal and reconstruct the deploy-intent refusal
The dispatch refusal added for unrecordable deploy intents dropped the
underlying cause from every operator-visible surface, leaked a corrupt
stored row as a thrown decode error into the deploy-failure arm, and
left the one post-commit arm whose classification recovery could not
reconstruct when the settle write failed in the same window: a bound
attempt with no intent row read as an apply-only completion and
reported false convergence there.
The intent hook now captures the scrubbed cause into the settled
reason, contains the decode failure as a read-back refusal, and
journals a deploy_intent_refused witness row (payload type
DeployIntentRefusedPayload, recorded by the new deployIntentRefused
transition) on every refusal path. reconstructBoundDispatchResult
checks the witness before the intent row, so an unsettled refusal
settles recovery_required with view-target-results and no deploy id,
matching the live arm. The refusal log line names the dispatch
operation id for correlation, and the stale comments claiming the
witness writes at the moment of the filesystem commit now describe the
post-commit interval and its unproven fallback honestly.
* test(gitops): pin refusal cause scrubbing and witness recovery arms
Extend the write-failure refusal test to assert the settled reason
names the journaling failure and the refusal witness row lands. The
simulated journaling error now carries a credential-shaped store URL
so the cause capture is pinned scrubbed in both the durable settled
reason and the server log. Turn the read-back variant into a
genuinely corrupt stored payload (undecodable after_json) so it pins
that the decode error is contained as a refusal rather than surfacing
as a deploy failure. Add the compounding cases: intent journal throws
and the refusal's settle write fails in the same window, leaving the
attempt open with the witness, and startup recovery settles it once
as recovery_required without promoting or deploying again; a
journaled-but-corrupt intent row with a lost settle is reconstructed
from the witness directly in storage, pinning that recovery checks
the refusal before decoding the intent so the corrupt row cannot
strand the attempt; and a refusal whose witness write also fails
still refuses, still settles, and logs that the witness is
unavailable.
* fix(gitops): journal deploy intent before fallible deploy preparation
The deploy-intent hook ran after the policy gate and the recovery
handoff, so a policy rejection or a failed handoff threw before the
intent existed. If the resulting deploy-failure settle was also lost,
startup recovery found a bound dispatch with no intent row and no
refusal witness, and projected the source state: a quiet
no-source-change convergence for a deploy the operator asked for that
never ran.
Move the hook to the top of the deploy branch so durable intent exists
before any step that can still stop it. Recovery already distinguishes
the new state: intent journaled without a deploy_started row settles
as a requested deploy with no deploy record, with a retry next action
and no claimed id. The refusal arm, the manual-apply untracked arm,
and the threaded-id behavior are unchanged; only the position moves.
* test(gitops): pin intent-first ordering in policy and handoff windows
Four dispatch tests for the two fallible steps that sit between the
bind and Compose, now that the deploy intent is journaled ahead of
them:
- Policy-gate block and recovery-handoff failure each settle live as a
deploy failure with the intent row durable, the refusal witness
absent, Compose never called, one settlement, and recovery adding
nothing to a settled attempt.
- With the live settle lost in the same window (policy block and
handoff failure), startup recovery reconstructs both from the intent
row plus the absent deploy_started row as a requested deploy with no
deploy record: blocked with retry, the exact commit, no deploy id,
never the source projection's quiet convergence. Repeated passes
keep one promotion and one settlement per attempt.
* fix(gitops): record the deploy request in the dispatch reservation
A store outage spanning the whole deploy branch (intent journaling,
refusal witness, and live settlement all failing together) left startup
recovery with bind-plus-no-intent, which reconstructed as the apply-only
source projection and quietly converged a deploy that never started.
The source_reconcile_started reservation now records, atomically and
before promotion, whether the dispatch asked the pipeline to deploy. The
key is written only when true, so apply-only dispatches and reservations
from before this fact keep the exact payload shape readers already
handle. Recovery consults it only when no deploy intent and no refusal
witness survived: a deploy-requested attempt then settles as
recovery_required naming the missing deploy record, never through the
apply-only projection.
* test(gitops): cover the deploy-request reservation fact in recovery
Adds the compound-outage regression: intent write, refusal-witness
write, and live settlement all fail; after recovery the attempt settles
once under its original id as recovery_required, with one promotion,
zero Compose calls, and no trip through the source projection.
Companion coverage pins the other two readers of the fact: an apply-only
dispatch whose settle was lost still reconstructs truthfully from the
source projection (no key written, no false deploy claim), and a legacy
marked reservation without the key recovers as apply-only without
throwing.
|
||
|
|
8cf10c45f0 |
fix(sso): preserve granular roles across enabled role synchronization (#1875)
Extend the sso_role_sync condition in SSOService.provisionUser so the IdP-derived role is applied only while the stored role is Admin or Viewer. Granular roles (deployer, node-admin, auditor) remain manually authoritative. Replace the misleading inline comment with one accurate block explaining the setting gate, coarse-role sync, granular-role preservation, and independent email sync. Backed by parameterized regression tests for all three granular roles. |
||
|
|
66b79b83f0 |
chore(deps): bump the all-npm-backend group with 8 updates (#1914)
* chore(deps): bump the all-npm-backend group across 1 directory with 10 updates Bumps the all-npm-backend group with 10 updates in the /backend directory: | Package | From | To | | --- | --- | --- | | [openid-client](https://github.com/panva/openid-client) | `6.8.7` | `6.8.8` | | [systeminformation](https://github.com/sebhildebrandt/systeminformation) | `5.33.6` | `5.33.8` | | [tar-stream](https://github.com/mafintosh/tar-stream) | `3.2.0` | `3.2.1` | | [undici](https://github.com/nodejs/undici) | `8.10.0` | `8.10.2` | | [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) | `26.4.0` | `26.4.1` | | [eslint](https://github.com/eslint/eslint) | `10.9.1` | `10.10.0` | | [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint) | `8.68.0` | `8.69.0` | | [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest) | `4.1.11` | `5.0.0` | | [@aws-sdk/client-ecr](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/clients/client-ecr) | `3.1121.0` | `3.1127.0` | | [@aws-sdk/client-s3](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/clients/client-s3) | `3.1121.0` | `3.1127.0` | Updates `openid-client` from 6.8.7 to 6.8.8 - [Release notes](https://github.com/panva/openid-client/releases) - [Changelog](https://github.com/panva/openid-client/blob/main/CHANGELOG.md) - [Commits](https://github.com/panva/openid-client/compare/v6.8.7...v6.8.8) Updates `systeminformation` from 5.33.6 to 5.33.8 - [Release notes](https://github.com/sebhildebrandt/systeminformation/releases) - [Changelog](https://github.com/sebhildebrandt/systeminformation/blob/master/CHANGELOG.md) - [Commits](https://github.com/sebhildebrandt/systeminformation/compare/v5.33.6...v5.33.8) Updates `tar-stream` from 3.2.0 to 3.2.1 - [Commits](https://github.com/mafintosh/tar-stream/compare/v3.2.0...v3.2.1) Updates `undici` from 8.10.0 to 8.10.2 - [Release notes](https://github.com/nodejs/undici/releases) - [Commits](https://github.com/nodejs/undici/compare/v8.10.0...v8.10.2) Updates `@types/node` from 26.4.0 to 26.4.1 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) Updates `eslint` from 10.9.1 to 10.10.0 - [Release notes](https://github.com/eslint/eslint/releases) - [Commits](https://github.com/eslint/eslint/compare/v10.9.1...v10.10.0) Updates `typescript-eslint` from 8.68.0 to 8.69.0 - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.69.0/packages/typescript-eslint) Updates `vitest` from 4.1.11 to 5.0.0 - [Release notes](https://github.com/vitest-dev/vitest/releases) - [Changelog](https://github.com/vitest-dev/vitest/blob/main/docs/releases.md) - [Commits](https://github.com/vitest-dev/vitest/commits/v5.0.0/packages/vitest) Updates `@aws-sdk/client-ecr` from 3.1121.0 to 3.1127.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-ecr/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-js-v3/commits/v3.1127.0/clients/client-ecr) Updates `@aws-sdk/client-s3` from 3.1121.0 to 3.1127.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-s3/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-js-v3/commits/v3.1127.0/clients/client-s3) --- updated-dependencies: - dependency-name: openid-client dependency-version: 6.8.8 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-npm-backend - dependency-name: systeminformation dependency-version: 5.33.8 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-npm-backend - dependency-name: tar-stream dependency-version: 3.2.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-npm-backend - dependency-name: undici dependency-version: 8.10.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-npm-backend - dependency-name: "@types/node" dependency-version: 26.4.1 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: all-npm-backend - dependency-name: eslint dependency-version: 10.10.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: all-npm-backend - dependency-name: typescript-eslint dependency-version: 8.69.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: all-npm-backend - dependency-name: vitest dependency-version: 5.0.0 dependency-type: direct:development update-type: version-update:semver-major dependency-group: all-npm-backend - dependency-name: "@aws-sdk/client-ecr" dependency-version: 3.1127.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-npm-backend - dependency-name: "@aws-sdk/client-s3" dependency-version: 3.1127.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-npm-backend ... Signed-off-by: dependabot[bot] <support@github.com> * fix(deps): hold tar-stream at 3.2.0 and vitest at 4.x pending type/peer fixes tar-stream 3.2.1 bundles index.d.ts that imports types from streamx, which ships no type definitions, so the bundled types shadow the working @types/tar-stream and break the build. vitest 5.0.0 promotes vite to a required peer dependency; the backend has no vite install. Both revert to main's ranges; the other 8 group bumps stand. --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|
|
d15bac1ec9 |
chore(deps): bump the all-npm-frontend group (#1907)
Bumps the all-npm-frontend group in /frontend with 11 updates: | Package | From | To | | --- | --- | --- | | [@xyflow/react](https://github.com/xyflow/xyflow/tree/HEAD/packages/react) | `12.11.5` | `12.11.6` | | [lucide-react](https://github.com/lucide-icons/lucide/tree/HEAD/packages/lucide-react) | `1.37.0` | `1.41.0` | | [motion](https://github.com/motiondivision/motion) | `13.1.1` | `13.2.0` | | [@testing-library/user-event](https://github.com/testing-library/user-event) | `14.6.6` | `14.6.7` | | [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) | `26.4.0` | `26.4.1` | | [@types/react-dom](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react-dom) | `19.2.5` | `19.2.7` | | [eslint](https://github.com/eslint/eslint) | `10.9.1` | `10.10.0` | | [eslint-plugin-react-refresh](https://github.com/ArnaudBarre/eslint-plugin-react-refresh) | `0.5.5` | `0.5.6` | | [globals](https://github.com/sindresorhus/globals) | `17.11.0` | `17.12.0` | | [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint) | `8.68.0` | `8.69.0` | | [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest) | `4.1.11` | `5.0.0` | Updates `@xyflow/react` from 12.11.5 to 12.11.6 - [Release notes](https://github.com/xyflow/xyflow/releases) - [Changelog](https://github.com/xyflow/xyflow/blob/main/packages/react/CHANGELOG.md) - [Commits](https://github.com/xyflow/xyflow/commits/@xyflow/react@12.11.6/packages/react) Updates `lucide-react` from 1.37.0 to 1.41.0 - [Release notes](https://github.com/lucide-icons/lucide/releases) - [Commits](https://github.com/lucide-icons/lucide/commits/1.41.0/packages/lucide-react) Updates `motion` from 13.1.1 to 13.2.0 - [Changelog](https://github.com/motiondivision/motion/blob/main/CHANGELOG.md) - [Commits](https://github.com/motiondivision/motion/compare/v13.1.1...v13.2.0) Updates `@testing-library/user-event` from 14.6.6 to 14.6.7 - [Release notes](https://github.com/testing-library/user-event/releases) - [Changelog](https://github.com/testing-library/user-event/blob/main/CHANGELOG.md) - [Commits](https://github.com/testing-library/user-event/compare/v14.6.6...v14.6.7) Updates `@types/node` from 26.4.0 to 26.4.1 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) Updates `@types/react-dom` from 19.2.5 to 19.2.7 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react-dom) Updates `eslint` from 10.9.1 to 10.10.0 - [Release notes](https://github.com/eslint/eslint/releases) - [Commits](https://github.com/eslint/eslint/compare/v10.9.1...v10.10.0) Updates `eslint-plugin-react-refresh` from 0.5.5 to 0.5.6 - [Release notes](https://github.com/ArnaudBarre/eslint-plugin-react-refresh/releases) - [Changelog](https://github.com/ArnaudBarre/eslint-plugin-react-refresh/blob/main/CHANGELOG.md) - [Commits](https://github.com/ArnaudBarre/eslint-plugin-react-refresh/compare/v0.5.5...v0.5.6) Updates `globals` from 17.11.0 to 17.12.0 - [Release notes](https://github.com/sindresorhus/globals/releases) - [Commits](https://github.com/sindresorhus/globals/compare/v17.11.0...v17.12.0) Updates `typescript-eslint` from 8.68.0 to 8.69.0 - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.69.0/packages/typescript-eslint) Updates `vitest` from 4.1.11 to 5.0.0 - [Release notes](https://github.com/vitest-dev/vitest/releases) - [Changelog](https://github.com/vitest-dev/vitest/blob/main/docs/releases.md) - [Commits](https://github.com/vitest-dev/vitest/commits/v5.0.0/packages/vitest) --- updated-dependencies: - dependency-name: "@xyflow/react" dependency-version: 12.11.6 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-npm-frontend - dependency-name: lucide-react dependency-version: 1.41.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-npm-frontend - dependency-name: motion dependency-version: 13.2.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-npm-frontend - dependency-name: "@testing-library/user-event" dependency-version: 14.6.7 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: all-npm-frontend - dependency-name: "@types/node" dependency-version: 26.4.1 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: all-npm-frontend - dependency-name: "@types/react-dom" dependency-version: 19.2.7 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: all-npm-frontend - dependency-name: eslint dependency-version: 10.10.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: all-npm-frontend - dependency-name: eslint-plugin-react-refresh dependency-version: 0.5.6 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: all-npm-frontend - dependency-name: globals dependency-version: 17.12.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: all-npm-frontend - dependency-name: typescript-eslint dependency-version: 8.69.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: all-npm-frontend - dependency-name: vitest dependency-version: 5.0.0 dependency-type: direct:development update-type: version-update:semver-major dependency-group: all-npm-frontend ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|
|
1771c3d911 |
chore(deps-dev): bump @playwright/test in the all-npm-root group (#1906)
Bumps the all-npm-root group with 1 update: [@playwright/test](https://github.com/microsoft/playwright). Updates `@playwright/test` from 1.62.1 to 1.63.0 - [Release notes](https://github.com/microsoft/playwright/releases) - [Commits](https://github.com/microsoft/playwright/compare/v1.62.1...v1.63.0) --- updated-dependencies: - dependency-name: "@playwright/test" dependency-version: 1.63.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: all-npm-root ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |