Skip to content

feat: add Infomaniak DNS provider support - #5257

Merged
narcisonunez merged 6 commits into
Dokploy:canaryfrom
mitc-gjuge:feat/infomaniak-dns-provider
Sep 4, 2026
Merged

narcisonunez merged 6 commits into
Dokploy:canaryfrom
mitc-gjuge:feat/infomaniak-dns-provider

Conversation

@mitc-gjuge

@mitc-gjuge mitc-gjuge commented Sep 1, 2026 •

Copy link
Copy Markdown
Contributor

What is this PR about?

Adds Infomaniak as a supported DNS provider alongside Cloudflare, AWS Route53 and Porkbun. Once connected, Dokploy can list an Infomaniak account's domains as zones and create, update and delete their records, the same flow already available for the other providers.

The implementation follows the existing DnsClient interface in packages/server/src/utils/dns/:

  • infomaniak.ts implements listZones, listRecords, upsertRecord, updateRecord, deleteRecord and testConnection. Zones come from /1/product?service_name=domain and records from the v2 /2/zones/{zone}/records endpoints, authenticating with a bearer API token.
  • A new infomaniak value was added to the DnsProviderType Postgres enum and an infomaniakDnsConfigSchema (apiToken) was added to the discriminated union in db/schema/dns-provider.ts, together with the Drizzle migration for the enum change.
  • The token is masked/merged the same way the existing providers do it in services/dns-provider.ts.
  • UI: added an Infomaniak icon and the provider's API Token field in the DNS provider dialog, plus registration in the provider selector.

Two details are specific to Infomaniak:

  • The v2 record endpoints are keyed by zone name, not by the numeric product id, so listZones maps each domain's customer_name to both the zone id and name.
  • Records are addressed by a source relative to the zone, with the apex represented as a bare root dot ("."). Names are translated between Dokploy's fully-qualified format and that representation internally, with the trailing dot handled. "" and "@" are also accepted on read so a hand-written record still round-trips.
  • TXT targets are stored quoted by the API, so they are unquoted on read and quoted on write; otherwise the quotes stack on every save.

This PR also fills in the missing Porkbun label in show-dns-providers.tsx, which fell back to displaying the raw enum value (porkbun instead of Porkbun) — one line, in the same map this PR had to touch anyway.

Checklist

Before submitting this PR, please make sure that:

Tested against a real Infomaniak account on a local dev instance: testConnection, listZones (5 zones), listRecords, record creation, upsert de-duplication, update and delete, all through the Dokploy UI's tRPC endpoints. The test zone was left byte-identical to its original state.

The apex handling was found by that testing: the first implementation assumed an empty source for the apex, which rendered apex records as ..example.com and, worse, meant an apex upsert never matched the existing record and would have created a duplicate apex record instead of updating it — on a zone with live MX or SPF records at the apex, that breaks mail. The fix is in a separate commit, and the round-trip was then verified against a live apex record with a byte-identical before/after snapshot.

pnpm -r run typecheck and the DNS test suite pass (78 tests, including 20 new Infomaniak tests covering all six DnsClient methods, apex handling in both directions, and TXT quoting).

Issues related (if applicable)

Closes #5255

Screenshots (if applicable)

N/A

Note for maintainers

#5256 adds OVHcloud the same way. Both PRs add a 0190_* enum migration and touch the same two providerLabels maps, so whichever merges second will need its migration regenerated and a trivial conflict resolved.

Greptile Summary

Adds Infomaniak DNS provider support across the server client, persisted provider configuration, migration metadata, dashboard forms, provider labels, icons, and DNS tests.

  • Implements authenticated zone and record listing plus record creation, update, deletion, and connection testing.
  • Normalizes Infomaniak apex-source aliases and TXT quoting.
  • Adds the Infomaniak provider enum/configuration and masks its API token.
  • Registers Infomaniak in the dashboard and adds comprehensive provider tests.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains; the previously reported apex-alias mismatch is fixed at current HEAD and covered for ".", "", and "@".

Reviews (2): Last reviewed commit: "Merge remote-tracking branch 'origin/can..." | Re-trigger Greptile

mitc-gjuge and others added 2 commits September 1, 2026 15:25
Adds Infomaniak alongside Cloudflare, AWS Route53 and Porkbun, following the
existing DnsClient interface in packages/server/src/utils/dns/.

- infomaniak.ts implements listZones, listRecords, upsertRecord, updateRecord,
  deleteRecord and testConnection against the Infomaniak API. Zones come from
  /1/product?service_name=domain and records from the v2 /2/zones/{zone}/records
  endpoints, which are keyed by zone name rather than by product id.
- A new `infomaniak` value was added to the DnsProviderType enum along with an
  infomaniakDnsConfigSchema (apiToken) in the discriminated union, plus the
  Drizzle migration for the enum change.
- The token is masked/merged like the other providers in services/dns-provider.ts.
- UI: Infomaniak icon and API Token field in the DNS provider dialog, plus
  registration in the provider selector.

Infomaniak's `source` is relative to the zone (empty for the apex), so record
names are translated between Dokploy's fully-qualified format and Infomaniak's
subdomain-only format internally, with the trailing dot handled. TXT targets are
stored quoted by the API and are unquoted on read / quoted on write so that
editing a record does not stack quotes on every save.

Also fills in the missing Porkbun label in show-dns-providers.tsx, which fell
back to displaying the raw enum value.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Verified against a live Infomaniak account: the API returns `source: "."` for
apex records, not "" as assumed.

Reading them back produced a doubled dot ("..example.com"), and writing "" meant
an apex upsert never matched the existing record, so it would have created a
duplicate apex record instead of updating it.

toSource now emits "." for the apex and toFqdn accepts ".", "" and "@" so a
hand-written record still round-trips.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
toSource always writes the apex as ".", but listRecords already accepted "" and
"@" as apex spellings on read. The upsert lookup compared sources strictly, so a
record stored under one of the other spellings would not have matched and the
upsert would have created a duplicate apex record instead of updating it.

Normalize the candidate's source before comparing, so read and match agree.

Reported by Greptile on Dokploy#5257.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
@narcisonunez narcisonunez added enhancement New feature or request requested-changes labels Sep 1, 2026
mitc-gjuge and others added 2 commits September 1, 2026 21:02
listZones used /1/product, which lego uses but the API docs don't: it is the
legacy singular route, it returns no pagination metadata, and it 401s for at
least some tokens. The documented /1/products returns `total`, `pages`,
`items_per_page` and `page`, and defaults to 15 domains per page, so an account
with more domains than that would silently lose zones.

Switch to the plural endpoint and walk every page. Verified against a live
account: forcing per_page=2 collects all 5 domains across 3 pages with no
duplicates.

Also inline the single-use createdId helper.

Both reported by @narcisonunez on Dokploy#5257.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
canary landed its own 0190 (0190_nappy_anita_blake), which collides with this
branch's 0190_perpetual_red_skull. Renaming would not have been enough: drizzle
applies migrations by comparing timestamps against the last applied one, so this
branch's older `when` would have been silently skipped on any deployment that
had already run canary's 0190, leaving the enum without its new value.

Regenerated as 0191_cool_christian_walker with a current timestamp.
@mitc-gjuge

Copy link
Copy Markdown
Contributor Author

Rebased on canary, which has since landed its own 0190_nappy_anita_blake.

Worth noting why the migration is renumbered rather than renamed: drizzle decides what to apply by comparing timestamps against the single most recent applied migration (Number(lastDbMigration.created_at) < migration.folderMillis in pg-core/dialect.js), and the hash is never used for that decision. This branch's 0190_perpetual_red_skull carried an older when than canary's 0190, so on any deployment that had already run canary's, it would have been silently skipped — the enum would never get its new value, and the failure would only surface later as invalid input value for enum "DnsProviderType".

Regenerated as 0191_cool_christian_walker with a current timestamp. Typecheck and the DNS suite pass.

Same trap applies to #5258, and to whichever of the two merges second.

@mitc-gjuge mitc-gjuge mentioned this pull request Sep 2, 2026
3 tasks done
@mitc-gjuge

Copy link
Copy Markdown
Contributor Author

Heads-up on the red pr-check (test): it isn't coming from this PR.

The same job fails identically on canary itself — run 33600473202, 40 minutes before mine — with the same single failure:

FAIL __test__/deploy/application.real.test.ts > deployApplication - REAL Execution Tests > should REALLY build with Dockerfile
ExecError: Command execution failed: ... git clone --branch main --depth 1 --progress https://github.com/Dokploy/examples.git ...
❌ [ERROR] Fail to clone the repository https://github.com/Dokploy/examples.git

Failed Tests 1 in both runs, same test, same clone step. The repo itself is fine — it's public and clones normally from outside CI (git ls-remote resolves main at 27d3648), so this looks like the runner failing to reach it rather than anything about the repository.

Nothing this PR touches goes near that test: the diff is limited to the DNS provider files, its own test file, and the migration. The DNS suite passes (83 tests), along with typecheck and format.

Mentioning it only so the red check doesn't read as a problem with the branch.

@mitc-gjuge

Copy link
Copy Markdown
Contributor Author

Correction to my earlier note on the red pr-check (test) — I had the cause wrong.

I read the ❌ [ERROR] Fail to clone the repository line in the ExecError as the actual failure and concluded the runner couldn't reach Dokploy/examples. That text is part of the shell script echoed back inside the error message, not its output, so it proved nothing about which step failed. #5271 has since identified and fixed the real cause: the getDockerContextPath change in #5231 returned . instead of null, which broke defaultContextPath inference in docker-file.ts and made the Docker builds in the suite fail. A code regression, not a network problem.

What I said that still holds is the part that mattered: the failure pre-existed on canary and did not come from this branch.

I've merged canary into this branch, so it now carries that fix. No new migrations landed in the meantime, so 0191_cool_christian_walker is still last and keeps its timestamp ahead of 0190_nappy_anita_blake. Typecheck passes on both packages and the DNS suite is green (83 tests).

I can't confirm what CI does with it: the workflow runs on this PR sit in action_required and need a maintainer to approve them, so nothing has executed since the rebase.

@narcisonunez

narcisonunez commented Sep 3, 2026 •

Copy link
Copy Markdown
Collaborator

@mitc-gjuge Did you test this manually? It's not working with a real account. check greptile comment in the description and upload screenshots to the PR

@mitc-gjuge

mitc-gjuge commented Sep 4, 2026 •

Copy link
Copy Markdown
Contributor Author

@mitc-gjuge Did you test this manually? It's not working with a real account. check greptile comment in the description and upload screenshots to the PR

Yes — tested manually against a real Infomaniak account, screenshots below.

1-providers-list

Infomaniak in the provider selector, with its icon and label.

2-zones

The five zones the account manages, with record counts.

3-records-olidian-io

The records of one zone.

That third one is the one worth looking at: apex records render as olidian.io, not ..olidian.io, and the TXT reads v=spf1 -all unquoted even though Infomaniak stores it as "v=spf1 -all". Those are the two bugs the live testing turned up and that be4a5da fixes.

On the Greptile summary in the description

It predates the fixes. It was generated on 2026-09-01T14:25 and Greptile hasn't re-run since, so the block still names the apex alias mismatch as blocking. That was fixed the same day in be4a5da — normalizeSource on the upsert lookup, plus a parametrized test over ".", "" and "@" — and I replied on its thread at the time. GitHub marks both of your own threads on this PR as outdated for the same reason: the code moved past them.

Your two points were fixed in 59a7484: the plural /1/products endpoint with pagination, and the single-use helper inlined.

On "it's not working with a real account"

I'd really like to know what you saw, because I can't reproduce it: which provider, which step, and the exact error message would help a lot.

@narcisonunez

narcisonunez commented Sep 4, 2026 •

Copy link
Copy Markdown
Collaborator

@greptileai review


async upsertRecord(config, record) {
const source = toSource(record.name, record.zoneId);
const existing = await listZoneRecords(config, record.zoneId);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Last thing and we should be good to go.

This will load all the data just to find a match. That can be expensive if you have a lot records. The api support filtering by type and source

https://developer.infomaniak.com/docs/api/get/2/zones/%7Bzone%7D/records

@narcisonunez
narcisonunez merged commit d7884c7 into Dokploy:canary Sep 4, 2026
5 checks passed
mitc-gjuge added a commit to mitc-gjuge/dokploy that referenced this pull request Sep 5, 2026
Dokploy#5257 merged, so canary now carries the Infomaniak provider and its
0191_cool_christian_walker. This branch's 0191_elite_ultragirl collided on the
number, and the conflicts were the usual "infomaniak and ovh added at the same
spot" across the enum, the config union, the client registry, SENSITIVE_FIELDS,
both providerLabels maps, the icons and the provider form — all resolved by
keeping both.

Regenerated as 0192_light_lake with a current timestamp rather than renaming the
file, so drizzle still applies it on a database that has already run canary's
0191.
mitc-gjuge added a commit to mitc-gjuge/dokploy that referenced this pull request Sep 5, 2026
upsertRecord pulled every record of a zone just to find the one it was about to
write. The API can filter, so ask it to.

The match is still verified locally, because filter[source] matches substrings
rather than exact values. Verified against a live account:

    filter[source]=autoconfig  -> [autoconfig]
    filter[source]=auto        -> [autoconfig, autodiscover]
    filter[source]=disco       -> [autodiscover]

Letting the filter decide would therefore have upserted "auto" onto the existing
"autoconfig" record. The filter narrows what is transferred; the exact
comparison still decides. Covered by a test that fails if the first candidate is
trusted.

The apex was the other thing worth checking, since Infomaniak writes it as ".":
filter[source]=. does return the apex records, and an upsert on an existing apex
TXT returns its id rather than creating a second one.

Suggested by @narcisonunez on Dokploy#5257.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
github-actions Bot pushed a commit to Bl4ckBl1zZ/dokploy that referenced this pull request Oct 12, 2026
# [0.42.0](v0.41.1...v0.42.0) (2026-10-12)

### Bug Fixes

* accept deploy webhook payloads up to 25 MB ([4396451](4396451))
* account for all style end-tag variants like </style/> ([13bb1ac](13bb1ac))
* account for whitespace in style tag regex to prevent XSS ([5606a2b](5606a2b))
* add clipboard addon to docker container terminal ([65198f6](65198f6)), closes [Dokploy#5065](https://github.com/Bl4ckBl1zZ/dokploy/issues/5065)
* add createEnvFile toggle to Compose, mirroring Application ([5c862f7](5c862f7))
* add env -i PATH prefix to fresh volumes down command ([c668f6c](c668f6c))
* add sourceType to apiCreateCompose and apiCreateApplication ([30c408b](30c408b))
* add unzip to arch server set up ([f21931d](f21931d))
* address PR feedback (SVG helper & AddOrganization crash) ([4877c4f](4877c4f))
* allow docker compose command chaining ([ac57e22](ac57e22))
* allow Option/Alt composed characters in terminals on macOS ([b203489](b203489))
* **api:** configurable body size limits for OpenAPI catch-all route ([d11735d](d11735d))
* **api:** reject non-integer body size env vars ([529f54e](529f54e))
* **api:** validate OpenAPI body size limits against Greptile findings ([8144e1a](8144e1a))
* **application:** remove preview deployments when deleting an application ([f6b0700](f6b0700))
* apply compose file patches in preview and domain injection ([bc935ae](bc935ae))
* apply watch path negations as a pattern set ([abc2439](abc2439))
* assign tags created from the project dialog ([0ea7de7](0ea7de7)), closes [Dokploy#5117](https://github.com/Bl4ckBl1zZ/dokploy/issues/5117)
* **auth:** block passkey authentication and email signup when SSO is enforced ([5f10ed6](5f10ed6))
* **auth:** enforce SSO on server-side sign-in endpoints ([b839e6d](b839e6d)), closes [Dokploy#5288](https://github.com/Bl4ckBl1zZ/dokploy/issues/5288)
* **auth:** enforce SSO on server-side sign-in endpoints ([Dokploy#5291](https://github.com/Bl4ckBl1zZ/dokploy/issues/5291)) ([5c0d3b8](5c0d3b8)), closes [Dokploy#5288](https://github.com/Bl4ckBl1zZ/dokploy/issues/5288)
* **auth:** link SCIM-provisioned users through SSO and surface sign-in errors ([a1a672b](a1a672b)), closes [Dokploy#4973](https://github.com/Bl4ckBl1zZ/dokploy/issues/4973)
* **auth:** prefetch SSO state in SSR to prevent layout flash ([9cae609](9cae609))
* **billing:** allow switching billing interval during trial ([c453ac3](c453ac3))
* **billing:** one reminder at 1 day, guarded by an advisory lock ([0372304](0372304))
* **billing:** pick a plan for the trial and warn before it expires ([95b2712](95b2712))
* **billing:** stop the reminder claim from rewriting stale metadata ([14e0597](14e0597))
* **billing:** update server inclusion descriptions for clarity ([e369541](e369541))
* **build:** exclude CLI-only auth config from server dist build ([d92de5a](d92de5a))
* **ci:** create release tags on the pushed commit instead of default branch ([98af5a2](98af5a2))
* **ci:** create release tags on the pushed commit instead of default branch ([472ebed](472ebed))
* **ci:** deploy postgres/mongo instead of start ([2643ae0](2643ae0))
* **ci:** generate OpenAPI spec from the release commit in sync-version ([ff9fa74](ff9fa74))
* **ci:** handle both old (flat) and new (nested) project.create response formats ([f29a1a1](f29a1a1))
* **ci:** pin pnpm and strip v prefix in sync-version job ([b6190ff](b6190ff))
* **ci:** push hotfix cherry-picks with PAT to satisfy branch rules ([06b67d4](06b67d4))
* **ci:** raise floor to v0.29.4, require environmentId from project.create ([f59f114](f59f114))
* **ci:** run dokploy install.sh as root, install version A directly via DOKPLOY_VERSION ([ade7a79](ade7a79))
* clear stale stack log container selection ([c77f6d8](c77f6d8))
* close terminal websocket when local container shell exits ([1053743](1053743))
* **cloud:** load HubSpot chat widget for startup plan users ([c231c29](c231c29))
* **cloud:** remove HubSpot chat widget when dashboard unmounts ([382fdfc](382fdfc))
* **compose:** clean domain labels across services ([67fa8e4](67fa8e4))
* **compose:** clean up mapping domain labels ([f14f12c](f14f12c))
* **compose:** move Fresh Volumes into a Danger Zone with typed confirmation ([38d157a](38d157a))
* **compose:** Preserve raw remote mounts ([e43d99e](e43d99e)), closes [Dokploy#4794](https://github.com/Bl4ckBl1zZ/dokploy/issues/4794)
* **compose:** regenerate mapping domain labels ([5ceb88b](5ceb88b))
* **compose:** remove stale labels for disabled domains ([37c8d64](37c8d64))
* correct add-invitation form reset logic and select state ([143dfca](143dfca))
* correct log-tail server resolution, drop unrelated Networks gating ([95b1afc](95b1afc))
* correct network monitoring chart labels and units ([a822db5](a822db5)), closes [Dokploy#5115](https://github.com/Bl4ckBl1zZ/dokploy/issues/5115)
* correct switch thumb translate-x for checked state ([76c2416](76c2416)), closes [Dokploy#5120](https://github.com/Bl4ckBl1zZ/dokploy/issues/5120)
* count only physical disks in host Block I/O stats ([Dokploy#5540](https://github.com/Bl4ckBl1zZ/dokploy/issues/5540)) ([0d3a396](0d3a396))
* deploy monitoring as a swarm service instead of a standalone container ([Dokploy#5055](https://github.com/Bl4ckBl1zZ/dokploy/issues/5055)) ([3848fa9](3848fa9))
* **deps:** bump next to 16.3.4 to patch critical RCE advisories ([4cd4b83](4cd4b83))
* detect billing plan across all active Stripe subscriptions ([e79239a](e79239a))
* detect network delete/recreate by Docker ID during sync ([63efbff](63efbff)), closes [Dokploy#5179](https://github.com/Bl4ckBl1zZ/dokploy/issues/5179)
* disable delete/unlink user button while mutation is pending ([fee0071](fee0071)), closes [Dokploy#5063](https://github.com/Bl4ckBl1zZ/dokploy/issues/5063)
* disable minimumReleaseAge (broke builds on unrelated AWS SDK deps) ([9412b52](9412b52)), closes [Dokploy#4679](https://github.com/Bl4ckBl1zZ/dokploy/issues/4679)
* disable recharts animation on monitoring charts ([bd7fb8f](bd7fb8f))
* **dns:** apply content normalization in upsert match logic to prevent duplicates ([df0986b](df0986b))
* **dns:** do not report a restored OVH record as lost ([2311f34](2311f34)), closes [Dokploy#5258](https://github.com/Bl4ckBl1zZ/dokploy/issues/5258)
* **dns:** extract and normalize prio for porkbun MX/SRV records to prevent duplicates ([94cd503](94cd503))
* **dns:** fallback to data fields comparison for cloudflare structured SRV/CAA records ([ea1c6fc](ea1c6fc))
* **dns:** keep the record table on a valid page while filtering ([130be40](130be40))
* **dns:** match apex records stored under any apex spelling ([be4a5da](be4a5da)), closes [Dokploy#5257](https://github.com/Bl4ckBl1zZ/dokploy/issues/5257)
* **dns:** match record content in upsertRecord to prevent overwriting existing records ([91db4e6](91db4e6))
* **dns:** paginate the documented Infomaniak products endpoint ([59a7484](59a7484)), closes [Dokploy#5257](https://github.com/Bl4ckBl1zZ/dokploy/issues/5257)
* **dns:** preserve every value of a Route53 record set ([38e855b](38e855b))
* **dns:** report an OVH zone refresh failure for what it is ([28f71e7](28f71e7)), closes [Dokploy#5258](https://github.com/Bl4ckBl1zZ/dokploy/issues/5258)
* **dns:** represent the Infomaniak apex as a root dot ([2bc22c5](2bc22c5))
* **dns:** restore the original OVH record when a type change fails ([80a6baf](80a6baf)), closes [Dokploy#5258](https://github.com/Bl4ckBl1zZ/dokploy/issues/5258)
* **dns:** send SRV and CAA values to Cloudflare as structured data ([db2eb4f](db2eb4f))
* **dns:** spell out the OVH right that lists zones ([f83097b](f83097b)), closes [Dokploy#5258](https://github.com/Bl4ckBl1zZ/dokploy/issues/5258)
* docker cleanup hangs silently on false-positive busy-wait match ([Dokploy#5051](https://github.com/Bl4ckBl1zZ/dokploy/issues/5051)) ([34c5b68](34c5b68)), closes [Dokploy#5044](https://github.com/Bl4ckBl1zZ/dokploy/issues/5044)
* domain validation false negative on servers with multiple IPs ([9761616](9761616)), closes [Dokploy#4658](https://github.com/Bl4ckBl1zZ/dokploy/issues/4658)
* **domain:** allow single-label hostnames without a TLD ([5d231bf](5d231bf))
* **domains:** add status tooltip to table ([92b628c](92b628c))
* **domains:** don't fetch AddDomain queries until the dialog opens ([0f28775](0f28775)), closes [Dokploy#5279](https://github.com/Bl4ckBl1zZ/dokploy/issues/5279)
* **domains:** include server interface addresses in validation ([e79ed76](e79ed76))
* don't auto-run Health diagnostics on mount ([c741ff3](c741ff3))
* don't cancel schedule deployments running outside the panel process on restart ([Dokploy#5053](https://github.com/Bl4ckBl1zZ/dokploy/issues/5053)) ([f12ecc3](f12ecc3)), closes [Dokploy#4986](https://github.com/Bl4ckBl1zZ/dokploy/issues/4986)
* don't leak git provider secrets to non-owner org members ([a426140](a426140))
* don't quote .env values for stack deploys ([d183018](d183018)), closes [Dokploy#4694](https://github.com/Bl4ckBl1zZ/dokploy/issues/4694) [Dokploy#5096](https://github.com/Bl4ckBl1zZ/dokploy/issues/5096) [Dokploy#5110](https://github.com/Bl4ckBl1zZ/dokploy/issues/5110)
* **editor:** consistent YAML auto-indent on newline ([7ca96f6](7ca96f6)), closes [Dokploy#4650](https://github.com/Bl4ckBl1zZ/dokploy/issues/4650)
* eliminate FOUC for whitelabeling across all pages ([26d716b](26d716b))
* enable tailwindDirectives in biome css parser ([ae2edc7](ae2edc7))
* enhance error handling and directory management in updateFileMount function ([e4012d8](e4012d8))
* escape env values written to the Compose .env file so $, #, and quotes survive literally ([ff39cb5](ff39cb5))
* exclude node_modules/.next from biome scanner, bump to 2.5.7 ([dced797](dced797))
* Fix server card title overflow for responsiveness ([f904a7b](f904a7b))
* **github-webhook:** apply preview limit only to new previews ([98dbc59](98dbc59)), closes [Dokploy#4074](https://github.com/Bl4ckBl1zZ/dokploy/issues/4074)
* **gitlab:** handle missing expires_in in OAuth token response ([fc5b8d0](fc5b8d0)), closes [Dokploy#4362](https://github.com/Bl4ckBl1zZ/dokploy/issues/4362)
* honour metaName for page titles and show a loading state in requests ([6204415](6204415))
* improve mount path warning for PostgreSQL by handling multiple data mounts ([c0694eb](c0694eb))
* improve responsive layout for application logs page ([24cc455](24cc455))
* invalid replacement placeholder in redirect form ([Dokploy#5050](https://github.com/Bl4ckBl1zZ/dokploy/issues/5050)) ([3fbd67f](3fbd67f))
* invalidate railpack build cache when env changes ([7da9681](7da9681))
* keep native log container id in sync when Swarm/compose replaces it ([cf0e8d2](cf0e8d2)), closes [Dokploy#5134](https://github.com/Bl4ckBl1zZ/dokploy/issues/5134)
* kill deployment log tail when the client disconnects early ([7b89d1f](7b89d1f))
* **libsql:** validate appName like the other database kinds ([06b3bb2](06b3bb2))
* **lint:** clear Biome 2.5.7 errors inherited from upstream canary ([9e4c4ee](9e4c4ee))
* list stack containers across swarm nodes via docker stack ps ([1f73ebd](1f73ebd))
* lock sidebar-inset scroll while a modal is open ([dc9c671](dc9c671)), closes [Dokploy#5064](https://github.com/Bl4ckBl1zZ/dokploy/issues/5064)
* **logs:** don't classify explicit non-error key/values as error ([faf600b](faf600b)), closes [Dokploy#4538](https://github.com/Bl4ckBl1zZ/dokploy/issues/4538)
* **logs:** require complete no-error values in colon pairs ([60112d8](60112d8))
* **logs:** respect log level explicitly declared by structured loggers ([dc2fc40](dc2fc40))
* make fallback og image url absolute for social scrapers ([15bf663](15bf663))
* mark session cookie as Secure when served over HTTPS ([f5c21db](f5c21db)), closes [Dokploy#4709](https://github.com/Bl4ckBl1zZ/dokploy/issues/4709)
* mkdir parent dir before writing file mount updates ([3a8fad5](3a8fad5)), closes [Dokploy#5152](https://github.com/Bl4ckBl1zZ/dokploy/issues/5152)
* **monitoring:** remove legacy container and default empty cronJob ([bee6918](bee6918))
* normalize email argument before lookup in reset-password ([8db39a8](8db39a8))
* **notifications:** add missing newline to ntfy database backup message ([341f38b](341f38b))
* **notifications:** send server threshold alerts through email, resend, gotify and ntfy channels ([3638c7b](3638c7b))
* **notifications:** use real server name in remote threshold alerts ([e19dd43](e19dd43))
* only pin --project-directory when compose has mounts ([ec8e645](ec8e645)), closes [Dokploy#5181](https://github.com/Bl4ckBl1zZ/dokploy/issues/5181) [Dokploy#5230](https://github.com/Bl4ckBl1zZ/dokploy/issues/5230) [Dokploy#5242](https://github.com/Bl4ckBl1zZ/dokploy/issues/5242) [Dokploy#5235](https://github.com/Bl4ckBl1zZ/dokploy/issues/5235) [Dokploy#5230](https://github.com/Bl4ckBl1zZ/dokploy/issues/5230) [Dokploy#5242](https://github.com/Bl4ckBl1zZ/dokploy/issues/5242)
* only sum currentPriceAmount from the identified plan subscription ([3f6dfba](3f6dfba))
* open deployments tab when opening a service from the deployments list ([60ad782](60ad782))
* parse Block I/O and Network I/O stats to numeric MB so monitoring charts render ([b3e7a8a](b3e7a8a))
* parse deploy webhook bodies in the handler so the size limit can be configured ([1c6d333](1c6d333))
* pass timezone when removing repeatable schedule job ([4e38925](4e38925))
* persist serverThreshold for gotify/ntfy and guard teams dispatch ([4730807](4730807))
* pin traefik routing to the isolated network when isolated deployment is enabled ([a8c35f7](a8c35f7))
* preserve ${VAR} interpolation in .env files ([452b7aa](452b7aa)), closes [Dokploy#5151](https://github.com/Bl4ckBl1zZ/dokploy/issues/5151)
* preserve application status when saving provider settings ([0d6e984](0d6e984))
* preserve compose status when saving provider settings ([Dokploy#5544](https://github.com/Bl4ckBl1zZ/dokploy/issues/5544)) ([560e790](560e790))
* preserve parameter expansion (${VAR:-default}) in .env interpolation ([b67519e](b67519e))
* preserve selected log container on refetch ([d7c152a](d7c152a))
* preserve selected stack log container ([a5b8626](a5b8626))
* preserve server schedule failures through tee pipeline ([eb80f79](eb80f79))
* preserve stack log selection on fetch failures ([2221b8a](2221b8a))
* prevent deployment commit hash overflow on mobile ([d6ec4bd](d6ec4bd))
* prevent Postgres 100-argument limit crash and restore data parity for restricted-member project access ([2cb499f](2cb499f))
* prevent unsaved organization form fields from resetting on window focus ([f46cd46](f46cd46))
* **preview-deployment:** refetch github provider before authenticating ([30e8d9b](30e8d9b)), closes [Dokploy#4898](https://github.com/Bl4ckBl1zZ/dokploy/issues/4898)
* reap HEALTHCHECK zombies by running tini as PID 1 ([dec05ac](dec05ac))
* redact S3 credentials in backup logs regardless of shell quoting ([6215c7f](6215c7f))
* redact S3 credentials in backup logs regardless of shell quoting ([Dokploy#5527](https://github.com/Bl4ckBl1zZ/dokploy/issues/5527)) ([916bb50](916bb50))
* remove empty routers/services traefik config instead of writing invalid yaml ([032e804](032e804)), closes [Dokploy#5189](https://github.com/Bl4ckBl1zZ/dokploy/issues/5189)
* remove Route53 endpoint override and pin AWS SDK to an aged version ([02edd6a](02edd6a))
* replace metaTitle with ogImageUrl in reset payloads ([13b043e](13b043e))
* report remote-node containers correctly in stack monitoring ([0efe7fe](0efe7fe))
* **requests:** guard RequestHost before filtering to avoid crash on malformed logs ([314a31d](314a31d))
* reset stack log selection when switching modes ([60612b0](60612b0))
* resolve @ to the zone's apex domain before sending it to the provider ([37f0bba](37f0bba))
* resolve 6 critical Dependabot alerts ([1958969](1958969))
* resolve environment variables on application rollback ([2739690](2739690))
* resolve relative bind mounts against code dir for git-based compose deploys ([87b9149](87b9149)), closes [Dokploy#5181](https://github.com/Bl4ckBl1zZ/dokploy/issues/5181)
* resolve security vulnerabilities in whitelabeling ([6282b6e](6282b6e))
* resolve SVG unicode base64 encoding error and image upload race conditions ([8806d4e](8806d4e))
* **restore:** drop USE/CREATE DATABASE statements so mysql/mariadb restores target the selected database ([2505c34](2505c34))
* return a valid docker host for docker desktop ([3e11897](3e11897))
* run database dump once per backup and clean up partial uploads on failure ([f633d4b](f633d4b))
* run Railpack install with sudo during remote deploy ([679dbf9](679dbf9)), closes [Dokploy#5007](https://github.com/Bl4ckBl1zZ/dokploy/issues/5007)
* satisfy HttpRouter type in traefik config test ([22450ab](22450ab))
* **schedule:** return deployment metadata from runManually and fail early on missing container ([cbb3450](cbb3450))
* **security:** escape compose domain error message to prevent command injection ([a83b202](a83b202))
* **security:** update Traefik from v3.6.7 to v3.6.20 ([cee6742](cee6742))
* **security:** update Traefik to v3.6.25 ([36db6f5](36db6f5))
* **server-health:** detect custom default-address-pools via docker info ([ea3f9f3](ea3f9f3)), closes [Dokploy#5383](https://github.com/Bl4ckBl1zZ/dokploy/issues/5383)
* **server:** allow plus, at, and valid path characters in readValidDirectory ([bd8ba12](bd8ba12))
* **server:** sanitize sshd config values in security audit JSON output ([09ea766](09ea766))
* **setup:** use Docker 29.4.2 on Ubuntu 26.04 to fix install failure ([b9a8064](b9a8064)), closes [Dokploy#4327](https://github.com/Bl4ckBl1zZ/dokploy/issues/4327) [Dokploy#4349](https://github.com/Bl4ckBl1zZ/dokploy/issues/4349) [Dokploy#4501](https://github.com/Bl4ckBl1zZ/dokploy/issues/4501)
* sort imports in domain.ts (biome) ([286938b](286938b))
* **sso:** trust admin-configured SSO providers for account linking ([67f6890](67f6890)), closes [Dokploy#5099](https://github.com/Bl4ckBl1zZ/dokploy/issues/5099)
* strip git provider secrets from compose.one response ([6239073](6239073))
* surface native stack inspection failures ([49055a0](49055a0))
* surface the error state instead of "No results." when the log query fails ([7e9b77e](7e9b77e))
* sync pnpm-lock.yaml with nanoid 3.3.18 ([ad00669](ad00669))
* sync terminal size with backend PTY, avoid connecting with placeholder container ID ([ff275fe](ff275fe)), closes [Dokploy#5092](https://github.com/Bl4ckBl1zZ/dokploy/issues/5092) [xtermjs/xterm.js#5011](xtermjs/xterm.js#5011)
* **terminal:** prevent terminal from collapsing on zoom ([5c19d1b](5c19d1b))
* tooltip incorrectly lists volumes among Daily Cleanup targets ([4032895](4032895)), closes [Dokploy#3267](https://github.com/Bl4ckBl1zZ/dokploy/issues/3267)
* **traefik:** skip empty service reconnect ([ede4626](ede4626))
* **typecheck:** fix twoFactorRedirect typescript error ([f23b5b9](f23b5b9))
* **ui:** align close button in web server update modal ([0c19973](0c19973))
* **ui:** change filter layout breakpoint to lg to prevent overflow on tablets ([1e9873b](1e9873b))
* **ui:** expose isStaticSpa toggle for nixpacks + publish directory ([94098b2](94098b2)), closes [Dokploy#4993](https://github.com/Bl4ckBl1zZ/dokploy/issues/4993)
* **ui:** keep dropdown menus open on window blur ([081cf3f](081cf3f))
* **ui:** persist trigger type selection in GitHub provider forms ([15bc4b5](15bc4b5))
* **ui:** prevent collapsed avatar clipping ([9999f0a](9999f0a)), closes [Dokploy#4946](Dokploy#4946)
* **ui:** prevent layout shift in log counter using tabular-nums ([bb0c59f](bb0c59f))
* **ui:** prevent tooltips from getting stuck when server modals close ([35d26a8](35d26a8))
* **ui:** remove early return to render theme-aware favicon immediately ([689ae51](689ae51))
* **ui:** resolve tooltips overlapping open modals on remote servers page ([a4a13b9](a4a13b9))
* **ui:** scope deployment delete loading ([268ec1f](268ec1f))
* **ui:** show real status code on error page ([325c2a4](325c2a4))
* **ui:** standardize settings layout widths to full width ([a6c1f30](a6c1f30))
* **ui:** structurally resolve unclickable watch path badges ([ae95485](ae95485))
* **ui:** track concurrent deployment deletes ([6e79270](6e79270))
* **ui:** truncate organization name properly in sidebar without clipping descenders ([bbca400](bbca400))
* **ui:** unify tab header styles across backups, schedules, volume backups and patches ([3951247](3951247))
* **ui:** update favicon dynamically based on app theme ([9be96d4](9be96d4)), closes [Dokploy#4100](https://github.com/Bl4ckBl1zZ/dokploy/issues/4100)
* **ui:** vertically align enable submodules and improve watchpath button a11y ([a94f8b9](a94f8b9))
* **ui:** widen restore backup dialog to match other backup dialogs ([5ae289c](5ae289c))
* use dockerode API instead of shell docker ps for changePassword container lookup ([41d355c](41d355c)), closes [Dokploy#5108](https://github.com/Bl4ckBl1zZ/dokploy/issues/5108)
* use lazy initializer for useState with function calls ([43f9f54](43f9f54))
* use proper linting ([f3c7edc](f3c7edc))
* use updateJob to replace repeatable schedule job on update in cloud ([91bcf88](91bcf88)), closes [Dokploy#5233](https://github.com/Bl4ckBl1zZ/dokploy/issues/5233)
* **vault:** expand Infisical secret references when listing secrets ([6e760da](6e760da))
* **vault:** read Infisical secrets pulled in through an import ([0c1dfe9](0c1dfe9)), closes [Dokploy#5413](https://github.com/Bl4ckBl1zZ/dokploy/issues/5413)
* verify server ownership before running IP detection ([4b7a629](4b7a629)), closes [Dokploy#5214](https://github.com/Bl4ckBl1zZ/dokploy/issues/5214)
* verify swarm task convergence before marking db deploys done ([5624dff](5624dff))
* **volume-backups:** preserve backup failure status ([c6ab76f](c6ab76f))
* **volume-backups:** restart services after backup failure ([2598a62](2598a62))
* warn when the postgres image implies a data directory that mismatches the mounted volume ([6a10923](6a10923))
* **webhook:** include added and removed files in watchPaths validation ([2df02f5](2df02f5))
* write remote traefik config via SFTP instead of exec+base64 ([68a0555](68a0555))

### Features

* account deletion for Dokploy Cloud ([a87a5c0](a87a5c0))
* add "Deploy with Fresh Volumes" button for Docker Compose projects ([36ba9fc](36ba9fc))
* add @aws-sdk/client-secrets-manager dependency to pnpm-lock.yaml ([fada032](fada032))
* add AWS Secrets Manager client dependency ([be9c826](be9c826))
* add baseUrl to github provider to support github enterprise url ([1cf81e3](1cf81e3))
* add DNS provider integration (Cloudflare, AWS Route53) ([4d58f39](4d58f39))
* add Docker events tab to docker dashboard ([daba2d7](daba2d7))
* add Docker Health diagnostics tab ([4bf6dff](4bf6dff))
* add drag-and-drop logo upload for organizations ([0393c26](0393c26))
* add dynamic open graph metadata for enterprise whitelabeling ([b5c3d86](b5c3d86))
* add dynamic open graph metadata for link previews ([Dokploy#5348](https://github.com/Bl4ckBl1zZ/dokploy/issues/5348)) ([2d5a6ef](2d5a6ef))
* add go-to-page input to Docker events pagination ([3be9cdf](3be9cdf))
* add Images and Disk Usage tabs to docker dashboard ([c2071bb](c2071bb))
* add Infomaniak DNS provider support ([ff3ebef](ff3ebef))
* add OpenPanel analytics to cloud version ([2f7f50a](2f7f50a))
* add Overview dashboard (Services, Backups & Domains tabs) ([046a608](046a608))
* add OVHcloud DNS provider support ([e629248](e629248))
* add Porkbun DNS provider support ([74d2d41](74d2d41))
* add session management page (list active sessions + revoke) ([7728e5e](7728e5e))
* add sorting and pagination to Docker events table ([fa7a54b](fa7a54b))
* add target_version input, replace extra_pairs field ([a504449](a504449))
* add upgrade integration test workflow (manual, matrix-based) ([55704ff](55704ff))
* add user filtering to session management and update search functionality ([a2329ae](a2329ae))
* add volumes section and file explorer for containers and volumes ([d0eb6ef](d0eb6ef))
* **ai:** add Perplexity integration attribution ([f2ba262](f2ba262))
* allow reset-password script to target a specific user by email ([dada304](dada304))
* **auth:** add passkey support via better-auth ([7b0bcee](7b0bcee))
* bulk import secrets from vault providers ([50acb79](50acb79))
* **ci:** add hotfix cherry-pick and release workflows ([2f8af90](2f8af90))
* cloud onboarding wizard, billing trial card, and post-checkout server setup ([2e2e0c8](2e2e0c8))
* **compose:** add pull latest images on deploy toggle ([49b8214](49b8214))
* **compose:** support AI models in Compose specification ([29cdc81](29cdc81))
* consecutive upgrade pairs, drop curated pair and extra_pairs input ([eaec680](eaec680))
* **dns:** rework provider management and support all record types ([9c4ad14](9c4ad14))
* **domains:** add enable/disable toggle for domains ([ee802cd](ee802cd))
* **domains:** show redeploy hint on all compose domain actions ([898af1f](898af1f))
* enable Google Tag Manager on cloud version only ([aedb35b](aedb35b))
* enhance vault provider access control and environment variable handling ([9747e83](9747e83))
* enhance vault provider management and environment variable resolution ([f662242](f662242))
* implement session management with filtering and sorting capabilities ([f6c1e77](f6c1e77))
* implement utility functions to streamline owner selection for backups and domains ([d770e3c](d770e3c))
* integrate vault provider management and environment variable resolution ([b8822a1](b8822a1))
* interactive feature showcase on the signup panel ([ac9ad72](ac9ad72))
* list associated services in delete server modal ([0276021](0276021))
* make organization selector larger and searchable ([Dokploy#5052](https://github.com/Bl4ckBl1zZ/dokploy/issues/5052)) ([3cad115](3cad115)), closes [Dokploy#5043](https://github.com/Bl4ckBl1zZ/dokploy/issues/5043)
* move deployments into an overview page tab ([518ee44](518ee44))
* **networks:** add MTU option to network creation ([69a5615](69a5615))
* **organization:** allow setting a default role for new members ([aaf8d7e](aaf8d7e))
* **permissions:** add server.terminal to decouple server access from root SSH ([bbde5eb](bbde5eb))
* refactor serviceColumns for project access and enhance data retrieval ([479e8bb](479e8bb))
* remove unnecesary checks for ip and comments ([14be318](14be318))
* restrict self-service account deletion to cloud organization owners ([318ad36](318ad36))
* track cloud sign_up event in dataLayer ([d8b7c1e](d8b7c1e))
* **transfer:** move services between servers with their volumes, mounts and config ([9dcdb53](9dcdb53))
* **trial-banner:** add trial banner component to display trial status in sidebar ([988f5b6](988f5b6))
* **trial-notifications:** add email notification for expiring trials and integrate with webhook ([a0e1001](a0e1001))
* **ui:** inline deployments filter bar and fix layout jumping ([26317e9](26317e9))
* **ui:** smart truncation tooltips, org name limits, and logo UI polish ([Dokploy#5345](https://github.com/Bl4ckBl1zZ/dokploy/issues/5345)) ([1a29694](1a29694))
* update icons for Sessions and S3 Destinations in the sidebar menu ([c61163d](c61163d))
* upgrade to TypeScript 7 and Next.js 16.3 ([71521f2](71521f2))
* **vault:** add AWS Parameter Store provider ([1d26fdd](1d26fdd))
* **vault:** add Phase.dev secrets provider ([80859dc](80859dc)), closes [Dokploy#5122](https://github.com/Bl4ckBl1zZ/dokploy/issues/5122)
* **vault:** add Scaleway Secret Manager provider ([f238347](f238347))
* **vault:** address an Infisical folder from the reference ([5611662](5611662))

### Performance Improvements

* **dns:** filter Infomaniak records server-side when upserting ([8409b8c](8409b8c)), closes [Dokploy#5257](https://github.com/Bl4ckBl1zZ/dokploy/issues/5257)

### Reverts

* go back to a literal 25mb body limit for deploy webhooks ([82545bf](82545bf))
budivoogt added a commit to budivoogt/dokploy that referenced this pull request Oct 24, 2026
* chore: automate git worktree setup (shared node_modules, per-worktree dev port)

* chore: add SKILL.md for issue reproduction and verification process
chore: create .mcp.json for MCP server configuration

* chore: add install-worktree-deps.sh script for dependency installation

* chore: add script to spawn agent worktree with environment setup

* fix(server): allow plus, at, and valid path characters in readValidDirectory

* [autofix.ci] apply automated fixes

* fix: preserve ${VAR} interpolation in .env files

Escaping in prepareEnvironmentVariablesForFile escaped every $,
including inside a deliberate ${VAR} reference, breaking Compose's
documented .env variable interpolation. Only escape $ that isn't
part of a ${IDENTIFIER} sequence.

Fixes Dokploy#5151

* fix: preserve parameter expansion (${VAR:-default}) in .env interpolation

* fix: detect network delete/recreate by Docker ID during sync

Network sync compared only by name, so a network deleted and
recreated with the same name but different attributes (driver,
attachable, etc.) was reported as already in sync.

Store Docker's network Id (dockerId column) and compare it against
the live Id during sync. A name match with a dockerId mismatch is
now surfaced as a new "changed" state, with an Update action to
refresh the DB row from the live network.

Fixes Dokploy#5179

* docs: add CLAUDE.md code style notes

* [autofix.ci] apply automated fixes

* fix: prevent unsaved organization form fields from resetting on window focus

* chore: remove obsolete .mcp.json configuration file

* fix: correct network monitoring chart labels and units

Network chart showed values labeled as KB/s, but the underlying data
is MB totals accumulated since boot. Convert to GB and relabel as
Network Total (since Boot).

Fixes Dokploy#5115

* fix: resolve relative bind mounts against code dir for git-based compose deploys

Compose deployments using a git provider (GitHub/GitLab/Gitea/Bitbucket/
custom Git) clone the repo verbatim, so composePath can point into a
subdirectory (e.g. deploy/docker-compose.yml). Docker Compose resolves
relative bind mounts (../files) against the compose file's own directory,
not against the code/ dir where file mounts are actually written, so the
mount silently binds an empty directory instead of the configured content.

Pin --project-directory to code/ when building the docker compose command
so relative mounts always resolve the same way raw deployments already do.

Fixes Dokploy#5181

* fix: remove empty routers/services traefik config instead of writing invalid yaml

Attaching Basic Auth or a Redirect to an application with no domains
wrote an empty routers/services block to the app's dynamic config
file. Traefik's file provider rejects that as invalid and aborts its
watcher, blocking config updates for every other application.

Fixes Dokploy#5189

* fix: mkdir parent dir before writing file mount updates

Fixes Dokploy#5152. updateFileMount wrote directly to the target path without
ensuring its parent directory existed, and silently swallowed errors
instead of surfacing them. If the path already existed as a directory
(e.g. Docker pre-creating a bind mount target), the write silently
failed and left an empty directory instead of the file.

* fix: enhance error handling and directory management in updateFileMount function

* Add journal entry for granting terminal permission to read roles

* fix: apply watch path negations as a pattern set

* [autofix.ci] apply automated fixes

* Revert "[autofix.ci] apply automated fixes"

This reverts commit a66684a.

* [autofix.ci] apply automated fixes

* fix: keep native log container id in sync when Swarm/compose replaces it

Closes Dokploy#5134. The container list queries in the Logs tab only fetched
once on mount, so if the underlying container was replaced (restart,
reschedule, redeploy) while the page stayed open, the native log
WebSocket kept streaming from the stale id and either froze or failed
with 'No such container'.

Poll the container list every 5s and reconcile the selected id against
it, matching the pattern already used for automatic selection.

* fix: disable delete/unlink user button while mutation is pending

Prevents double-submit when clicking Confirm on the Delete/Unlink User
dialog before the request resolves (issue Dokploy#5063).

* [autofix.ci] apply automated fixes

* fix: close terminal websocket when local container shell exits

* fix: report remote-node containers correctly in stack monitoring

Free monitoring resolved containers via a local Dockerode listContainers()
call, which only sees containers scheduled on the host running Dokploy.
For Stack services with tasks on other Swarm nodes, this always fell into
the "Container not running" branch even when the task was healthy
elsewhere, since local docker.listContainers() can't see remote-node
containers.

Now falls back to `docker service ps` (Swarm-aggregated, works from any
manager regardless of task placement) to tell a genuinely stopped
container apart from one running on another node, and reports that
distinction in the WS close reason instead of the misleading message.

Also fixes a client-side race where the stats websocket connected with
an empty appName on first mount (before the container selector settled),
and surfaces the close reason as a toast instead of only logging it.

* fix: domain validation false negative on servers with multiple IPs

Validate against the server's SSH IP plus its detected public egress
IP, instead of only the stored SSH address.

Fixes Dokploy#4658

* fix: satisfy HttpRouter type in traefik config test

Test routers were missing the required "service" field, and
mock.calls[0] destructuring failed under noUncheckedIndexedAccess.

* fix: verify server ownership before running IP detection

validateDomain accepted an arbitrary serverId and used it to look up
the server and SSH into it, without checking it belonged to the
caller's active organization. A member with domain:read could probe
other orgs' servers and get their SSH/detected IPs back in the
response.

Addresses greptile review on Dokploy#5214

* feat: add OpenPanel analytics to cloud version

* Bump version from v0.30.2 to v0.30.3

* fix: detect billing plan across all active Stripe subscriptions

getCurrentPlanForUser and getProducts only inspected subscriptions.data[0],
so a customer with more than one active Stripe subscription (e.g. Startup
plus a separately purchased additional server) could have their plan
resolved from the wrong subscription, resulting in currentPlan !== "startup"
and the HubSpot chat bubble not rendering.

* fix: only sum currentPriceAmount from the identified plan subscription

Summing across all active subscriptions could mix amounts with
different billing intervals (isAnnualCurrent only reflects the
matched plan subscription), producing an inconsistent total.

* fix/docker-context-path-default: Placeholder with name attribute "dockerContextPath" lied that the default path is ".", while being the Dockerfile directory.

* feat: allow reset-password script to target a specific user by email

Previously reset-password only reset the owner account's password.
Passing an email as an argument now resets that specific user's
password instead, while omitting it keeps the existing owner-reset
behavior.

Also scopes the update to the credential (password-based) account
row via providerId, and fixes the success check to verify a row was
actually updated instead of always reporting success.

* [autofix.ci] apply automated fixes

* fix: normalize email argument before lookup in reset-password

Trims and lowercases the CLI email arg to match the normalization
used elsewhere for user emails, so a differently-cased or
whitespace-padded email no longer falsely reports "User not found".

* fix: surface the error state instead of "No results." when the log query fails

The loading branch added in the previous commit split the fallback cell
two ways: spinner while in flight, "No results." otherwise. That second
branch also catches the failure case -- when readStatsLogs errors,
statsLogs stays undefined and isLoading goes false, so a failed request
renders as a successful empty response.

The query's isError/error were not destructured. The cell now branches
three ways and reports the error through AlertBlock, matching how
ShowTraefikSystem surfaces a failed readDirectories query.

* fix: prevent deployment commit hash overflow on mobile

* [autofix.ci] apply automated fixes

* Merge pull request Dokploy#5237 from shuv-o/feat/reset-password-by-email

feat: allow reset-password script to reset a specific user by email
(cherry picked from commit 5345c00)

[skip ci]

* fix failing test

* fix: only pin --project-directory when compose has mounts

--project-directory was added unconditionally in 87b9149 to fix
relative bind mount resolution for git-based compose deploys with a
nested composePath (Dokploy#5181). It also moves where build.context and the
generated .env resolve, breaking any compose file in a subdirectory
that has context: . alongside its Dockerfile (Dokploy#5230), or interpolates
env vars (Dokploy#5242).

Only pin --project-directory when the compose actually has mounts
configured; otherwise build.context and .env resolve against the
compose file's own directory like plain docker compose. Also pass
--env-file explicitly pointing at the generated .env next to the
compose file, so it's found even when --project-directory is pinned
(credit: tonnenpinguin, PR Dokploy#5235).

Fixes Dokploy#5230
Fixes Dokploy#5242

* fix: persist serverThreshold for gotify/ntfy and guard teams dispatch

* fix: use updateJob to replace repeatable schedule job on update in cloud

Fixes Dokploy#5233. Updating an enabled schedule (cron/timezone change) called
schedule() (create-backup), which only adds a new repeatable job and
never removes the one registered under the old cron pattern, leaving
an orphaned BullMQ job running in Redis indefinitely. Switched to
updateJob() (update-backup), which looks up the currently registered
repeatable pattern, removes it, then re-adds — same pattern already
used in backup.ts for backups.

* fix: pass timezone when removing repeatable schedule job

removeJob() for type 'schedule' computed the BullMQ repeatable key
using only the cron pattern, without the timezone. scheduleJob()
always registers the repeatable job with tz set (job.timezone || UTC),
so BullMQ's repeat key (name:jobId:endDate:tz:pattern) never matched
on removal — removeRepeatable silently no-op'd and the job kept
firing forever, surviving disable, delete, and reboots.

Forward timezone through removeJob (queue.ts), the /update-backup
lookup (index.ts), and the disable/delete call sites in the
schedule router.

* [autofix.ci] apply automated fixes

* fix: write remote traefik config via SFTP instead of exec+base64

Embedding the full YAML (base64-encoded) into a single SSH exec command
silently fails once the payload passes the SSH transport's packet size
ceiling (~90-100KB), truncating the dynamic config with no error
surfaced anywhere. Switch to SFTP, which has no such limit.

* fix: verify swarm task convergence before marking db deploys done

* Remove unused journal entry for phase vault provider from _journal.json

* Add entry for volatile piledriver in journal with version 7

* [autofix.ci] apply automated fixes

* refactor: remove DnsPageTransition component and update loading indicators in DNS-related components

* Remove unused journal entry for porkbun DNS provider from _journal.json

* Add new journal entry for wooden_nextwave version 7

* bump version to v0.30.4 in package.json

* feat: bulk import secrets from vault providers

Adds a modal to select and import multiple secrets from an assigned
vault provider at once, instead of typing each ${{vault.x.y}} reference
by hand. Existing keys are skipped by default and can be overridden per
row; also adds an "Access all" shortcut to the vault provider assignment
picker.

* test: fix stale traefik remote-write test expectation

writeTraefikConfigRemote moved from an execAsyncRemote echo command to
writeFileRemote (SFTP) a while back; the test still mocked the old
execAsyncRemote path and asserted on an echo command that no longer runs.

* feat: list associated services in delete server modal

Show the services attached to a remote server directly in its delete
confirmation modal, with a link to each service and a per-service
delete action, instead of only showing a generic 'has active
services' blocker.

* fix: don't leak git provider secrets to non-owner org members

gitlab.one, github.one, gitea.one and bitbucket.one returned the full
DB record (OAuth access/refresh tokens, client secrets, private keys,
webhook secrets, app passwords) to any org member who merely had
access to *use* a shared provider (sharedWithOrganization: true),
not just its owner or an org owner/admin.

Add canViewGitProviderSecrets() and null out the secret fields in
each .one response when the caller isn't the provider owner or an
org owner/admin.

* fix: strip git provider secrets from compose.one response

findComposeById embedded the full github/gitlab/bitbucket/gitea
relations (client secrets, OAuth tokens, private keys, app passwords)
and compose.one only used canEditDeployGitSource to set a
hasGitProviderAccess flag, never to hide the fields — so any member
with read access to a compose service got the connected git
provider's raw credentials, regardless of their access to that
provider itself.

Exclude the same secret columns findApplicationById already excludes.
Deploys are unaffected: the actual clone step always re-fetches the
provider fresh by id (findGithubById/findGitlabById/...), it never
reads secrets off the embedded relation.

* feat: add Infomaniak DNS provider support

Adds Infomaniak alongside Cloudflare, AWS Route53 and Porkbun, following the
existing DnsClient interface in packages/server/src/utils/dns/.

- infomaniak.ts implements listZones, listRecords, upsertRecord, updateRecord,
  deleteRecord and testConnection against the Infomaniak API. Zones come from
  /1/product?service_name=domain and records from the v2 /2/zones/{zone}/records
  endpoints, which are keyed by zone name rather than by product id.
- A new `infomaniak` value was added to the DnsProviderType enum along with an
  infomaniakDnsConfigSchema (apiToken) in the discriminated union, plus the
  Drizzle migration for the enum change.
- The token is masked/merged like the other providers in services/dns-provider.ts.
- UI: Infomaniak icon and API Token field in the DNS provider dialog, plus
  registration in the provider selector.

Infomaniak's `source` is relative to the zone (empty for the apex), so record
names are translated between Dokploy's fully-qualified format and Infomaniak's
subdomain-only format internally, with the trailing dot handled. TXT targets are
stored quoted by the API and are unquoted on read / quoted on write so that
editing a record does not stack quotes on every save.

Also fills in the missing Porkbun label in show-dns-providers.tsx, which fell
back to displaying the raw enum value.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>

* feat: add OVHcloud DNS provider support

Adds OVHcloud alongside Cloudflare, AWS Route53 and Porkbun, following the
existing DnsClient interface in packages/server/src/utils/dns/.

- ovh.ts implements listZones, listRecords, upsertRecord, updateRecord,
  deleteRecord and testConnection against the /domain/zone endpoints of the
  OVHcloud API, on any of its seven regional endpoints (ovh-eu/ca/us, kimsufi
  and soyoustart).
- A new `ovh` value was added to the DnsProviderType enum along with an
  ovhDnsConfigSchema (endpoint, applicationKey, applicationSecret, consumerKey)
  in the discriminated union, plus the Drizzle migration for the enum change.
- The application secret and the consumer key are masked/merged like the other
  providers' secrets in services/dns-provider.ts.
- UI: OVHcloud icon, an endpoint selector and the three credential fields in the
  DNS provider dialog, plus registration in the provider selector.

Three OVH-specific behaviours are handled explicitly:

- Requests are signed with `$1$` + sha1(applicationSecret+consumerKey+method+
  url+body+timestamp). The timestamp comes from the API's own clock via an
  unauthenticated GET /auth/time, since a host clock a few seconds off would get
  every call rejected; the measured drift is cached per endpoint for an hour.
- OVH only applies zone changes once the zone is explicitly refreshed, so every
  successful create, update and delete is followed by POST /domain/zone/{zone}
  /refresh.
- The record update payload carries no fieldType, so changing a record's type
  replaces the record (DELETE then POST) and returns the new id.

The record listing endpoint returns ids only, so each record is fetched
individually with the fan-out capped at 8 concurrent requests.

Also fills in the missing Porkbun label in show-dns-providers.tsx, which fell
back to displaying the raw enum value.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>

* fix(dns): represent the Infomaniak apex as a root dot

Verified against a live Infomaniak account: the API returns `source: "."` for
apex records, not "" as assumed.

Reading them back produced a doubled dot ("..example.com"), and writing "" meant
an apex upsert never matched the existing record, so it would have created a
duplicate apex record instead of updating it.

toSource now emits "." for the apex and toFqdn accepts ".", "" and "@" so a
hand-written record still round-trips.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>

* fix(dns): match apex records stored under any apex spelling

toSource always writes the apex as ".", but listRecords already accepted "" and
"@" as apex spellings on read. The upsert lookup compared sources strictly, so a
record stored under one of the other spellings would not have matched and the
upsert would have created a duplicate apex record instead of updating it.

Normalize the candidate's source before comparing, so read and match agree.

Reported by Greptile on Dokploy#5257.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>

* fix(dns): restore the original OVH record when a type change fails

Changing a record's type deletes the record then recreates it with the new type,
because OVH's update payload carries no fieldType. If the creation failed the
name was left with nothing and no rollback.

The delete still has to come first, since OVH rejects a CNAME that would sit
alongside other data on the same name. So on a failed creation the original
record is put back from the copy already fetched before the delete, and the
original error is rethrown. If the restore fails too, the error names the record
that has to be recreated by hand.

Reported by Greptile on Dokploy#5258.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>

* Cleanup leftover unused code in side.tsx

* fix(dns): paginate the documented Infomaniak products endpoint

listZones used /1/product, which lego uses but the API docs don't: it is the
legacy singular route, it returns no pagination metadata, and it 401s for at
least some tokens. The documented /1/products returns `total`, `pages`,
`items_per_page` and `page`, and defaults to 15 domains per page, so an account
with more domains than that would silently lose zones.

Switch to the plural endpoint and walk every page. Verified against a live
account: forcing per_page=2 collects all 5 domains across 3 pages with no
duplicates.

Also inline the single-use createdId helper.

Both reported by @narcisonunez on Dokploy#5257.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>

* fix(dns): spell out the OVH right that lists zones

OVH matches access rules per exact path: a `GET /domain/zone/*` rule grants the
subtree but not the bare `GET /domain/zone` that listZones and testConnection
call. Verified against a live account with a consumer key carrying that single
wildcard rule:

    GET /domain/zone                     -> 403 This call has not been granted
    GET /domain/zone/                    -> 200
    GET /domain/zone/{zone}/record       -> 200

The form only asked for rights on `/domain/zone/*`, so a token created by
following it could not list zones at all, and the failure surfaced as a bare
"This call has not been granted" that points nowhere.

The hint now lists the five rights verbatim, and a token missing the root one
gets an error that names it instead of echoing OVH's message.

Reported by @narcisonunez on Dokploy#5258.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>

* fix: resolve security vulnerabilities in whitelabeling

- Sanitize customCss in _document.tsx to prevent XSS breakouts by stripping </style> tags.
- Remove SSR favicon fetching to prevent SSRF and internal network scanning, falling back to client-side fetching.

* [autofix.ci] apply automated fixes

* fix: account for whitespace in style tag regex to prevent XSS

* fix: account for all style end-tag variants like </style/>

* [autofix.ci] apply automated fixes

* fix: resolve SVG unicode base64 encoding error and image upload race conditions

* [autofix.ci] apply automated fixes

* chore(api): add local Inngest dev server script

* feat: cloud onboarding wizard, billing trial card, and post-checkout server setup

- Onboarding wizard (Welcome -> Plan -> Project -> Server -> Deploy ->
  Complete), shown once to an org owner with zero projects and no active
  plan/trial; skippable per step or entirely
- Billing page shows the org's current plan, and a no-card 14-day trial
  card when eligible
- Post-checkout "Welcome to Dokploy Cloud" modal simplified to reuse the
  onboarding wizard's own project/server/deploy steps behind a modal
  instead of its previous standalone 6-step flow, using the app's regular
  typography instead of the wizard's display serif
- Onboarding wizard validates a persisted project still exists before
  resuming a stale session, and the dashboard layout no longer gets stuck
  redirecting to /dashboard/home once the local onboarding-active flag
  goes stale mid-session
- onboardingCompletedAt column on user, with a backfill so existing users
  aren't shown the wizard
- pnpm reset-onboarding dev script to reset a test account's onboarding
  state end to end

* [autofix.ci] apply automated fixes

* feat(vault): add AWS Parameter Store provider

* [autofix.ci] apply automated fixes

* bump version to v0.30.5

* Remove unused journal entry for wide Korg from _journal.json

* Remove unused journal entry for tag "0190_nappy_anita_blake" from _journal.json

* test: fix CI failures and improve local test robustness

* [autofix.ci] apply automated fixes

* fix: Fix server card title overflow for responsiveness

* fix(domains): include server interface addresses in validation

* refactor: extract createServerSideHelpers into a reusable function

* fix: address PR feedback (SVG helper & AddOrganization crash)

* refactor: extract resizeImage to shared utility

* chore(deps): bump nanoid

Bumps the npm_and_yarn group with 1 update in the /packages/server directory: [nanoid](https://github.com/ai/nanoid).


Updates `nanoid` from 3.3.11 to 3.3.18
- [Release notes](https://github.com/ai/nanoid/releases)
- [Changelog](https://github.com/ai/nanoid/blob/3.3.18/CHANGELOG.md)
- [Commits](ai/nanoid@3.3.11...3.3.18)

---
updated-dependencies:
- dependency-name: nanoid
  dependency-version: 3.3.18
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <[email protected]>

* fix: sync pnpm-lock.yaml with nanoid 3.3.18

The nanoid specifier in packages/server/package.json was bumped to
3.3.18 but the lockfile entry was not updated, causing
pnpm install --frozen-lockfile to fail in CI.

* fix(ui): truncate organization name properly in sidebar without clipping descenders

* [autofix.ci] apply automated fixes

* fix(auth): enforce SSO on server-side sign-in endpoints

Resolves Dokploy#5288.

- In better-auth hooks.before, reject /sign-in/email and /sign-in/social with FORBIDDEN when webServerSettings.enforceSSO is enabled
- Prevents direct API password authentication when SSO enforcement is enabled

* [autofix.ci] apply automated fixes

* [autofix.ci] apply automated fixes

* docs: fix reset-password command invocation in CONTRIBUTING

* docs: drop stale .mcp.json reference in fix-issue skill

* chore: remove unused filterEmptyValues and hasValues utils

* fix(auth): block passkey authentication and email signup when SSO is enforced

- Include /sign-in/passkey, /passkey/verify-authentication, /passkey/generate-authenticate-options, and /sign-up/email in enforced SSO block
- Prevents registered passkeys from bypassing enforced SSO policies

* feat(ui): smart truncation tooltips, org name limits, and logo UI polish (Dokploy#5345)

* feat: add dynamic open graph metadata for enterprise whitelabeling

* chore: use compressed open graph image

* [autofix.ci] apply automated fixes

* fix: replace metaTitle with ogImageUrl in reset payloads

* fix: make fallback og image url absolute for social scrapers

* [autofix.ci] apply automated fixes

* chore: revert accidental package.json changes

* fix(dns): report an OVH zone refresh failure for what it is

refreshZone runs after the record write has already succeeded, so a failure
there means the change exists at the provider but is not being served yet. It
was left unguarded at all six call sites, so the caller saw a bare transport
error and would reasonably read it as "nothing was applied" — while a type
change had already deleted the old record and created its replacement.

Rolling the write back would destroy correct state over a publish failure, and
re-deleting after an uncertain network error is how records get lost. The error
now says what actually happened and how it resolves: the next successful change
to the zone publishes it, or the user can refresh the zone from the OVH manager.

Guarding the helper covers upsertRecord and deleteRecord too, not just the type
change Greptile pointed at.

Reported by Greptile on Dokploy#5258.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>

* fix(dns): do not report a restored OVH record as lost

restoreRecord had the restore POST and its zone refresh inside one catch. That
was harmless while refreshZone swallowed failures, but the previous commit made
it throw, which brought a new case into that catch: the restore succeeds and
only the publication fails.

The message then told the user the record "has been deleted" and to recreate it
by hand. It exists at OVH, just unpublished, so following that advice duplicates
it as soon as the zone is refreshed.

The two failures are now reported separately. A failed POST still means the
record is really gone and prints what to recreate. A failed refresh after a
successful restore says the record is back but not served yet, and explicitly
says not to recreate it. Either way the original replacement error is kept, so
the user still learns why the type change failed.

Reported by Greptile on Dokploy#5258.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>

* chore: drop local scratch files committed by mistake

A git add -A while merging canary swept in .watch/, a local scratch directory
holding monitoring scripts, screenshots and draft comment text. None of it
belongs in this PR.

* perf(dns): filter Infomaniak records server-side when upserting

upsertRecord pulled every record of a zone just to find the one it was about to
write. The API can filter, so ask it to.

The match is still verified locally, because filter[source] matches substrings
rather than exact values. Verified against a live account:

    filter[source]=autoconfig  -> [autoconfig]
    filter[source]=auto        -> [autoconfig, autodiscover]
    filter[source]=disco       -> [autodiscover]

Letting the filter decide would therefore have upserted "auto" onto the existing
"autoconfig" record. The filter narrows what is transferred; the exact
comparison still decides. Covered by a test that fails if the first candidate is
trusted.

The apex was the other thing worth checking, since Infomaniak writes it as ".":
filter[source]=. does return the apex records, and an upsert on an existing apex
TXT returns its id rather than creating a second one.

Suggested by @narcisonunez on Dokploy#5257.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>

* Remove unused journal entry for "confused_rocket_racer" from _journal.json

* Add new journal entry for "0193_chemical_the_liberteens" with version 7

* fix(ui): align close button in web server update modal

Keep the close control in the header row so it lines up with the title and version badge instead of overlapping them.

* fix(vault): expand Infisical secret references when listing secrets

A secret in Infisical can reference another one (`${env.folder.KEY}`), which
is the only way to keep a value in one place and read it from several folders.
The Infisical client fetches `/api/v3/secrets/raw` without
`expandSecretReferences`, and that endpoint leaves references untouched by
default. The referencing secret therefore arrives as the literal `${...}`
string, is written to the generated `.env`, and the deployment still reports
success — the service just receives a placeholder instead of its value.

This is easy to miss because the single-secret endpoint (`/raw/{name}`) does
expand by default, so the Infisical UI and API show the resolved value while
only Dokploy sees the literal.

Adds the parameter and the first tests for this client: the list request now
asserts the flag, a missing secret and an auth failure. Without the fix the
new test fails with `expected null to be 'true'`.

* Reduce trial duration from 14 to 7 days

* fix(domains): don't fetch AddDomain queries until the dialog opens

Fixes Dokploy#5279. The Domains table (and grid) view mounts one AddDomain
instance per row for the edit action. Its 4 queries (domain.one,
application.one/compose.one, domain.canGenerateTraefikMeDomains,
compose.loadServices) ran unconditionally on mount instead of only
when the edit dialog is open, so any table interaction that causes a
re-render (typing in the host filter, sorting, toggling columns) fired
all 4 queries for every domain row again.

* fix(billing): update server inclusion descriptions for clarity

* feat(trial-banner): add trial banner component to display trial status in sidebar

* fix(sso): trust admin-configured SSO providers for account linking

better-auth's account-linking guard requires either the IdP asserting
email_verified or the sso_provider row being domainVerified before it
will link an SSO login to an existing local user with the same email.
Entra ID never asserts email_verified (neither via its OIDC userinfo
endpoint nor via SAML attributes), and Dokploy never persisted
domainVerified at all, so linking was permanently impossible for any
Entra-backed provider (OIDC or SAML) regardless of the local user's
own emailVerified state.

Add the domainVerified column, defaulting to true — Dokploy already
scopes providers to an admin-configured domain and gates registration
behind enterprise/admin permissions, so that domain match (still
enforced by better-auth's validateEmailDomain check) is trust enough.

Fixes Dokploy#5099

* fix(dns): match record content in upsertRecord to prevent overwriting existing records

Previously, upsertRecord matched only by type and name, silently
overwriting any existing record of the same type/name with different
content. This caused data loss when users had multiple records of the
same type (e.g. multiple TXT records for SPF + site verification).

Now upsertRecord also checks the record content/target before deciding
to update. If the content differs, a new record is created alongside
the existing one instead of replacing it.

Affected providers: Cloudflare, Porkbun, Infomaniak, OVH.
Tests updated accordingly.

* fix(dns): apply content normalization in upsert match logic to prevent duplicates

* fix(dns): fallback to data fields comparison for cloudflare structured SRV/CAA records

* fix(dns): extract and normalize prio for porkbun MX/SRV records to prevent duplicates

* [autofix.ci] apply automated fixes

* Update packages/server/src/utils/dns/porkbun.ts

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* [autofix.ci] apply automated fixes

* feat(vault): address an Infisical folder from the reference

An Infisical provider is pinned to one non-recursive `secretPath`, so reading
two folders means two providers, two machine identities and two sets of
credentials to rotate. This lets a reference name the folder instead:

    ${{vault.my-provider.external/sentry:SENTRY_DSN}}

`<path>:<KEY>` mirrors the HashiCorp client in this directory, which already
documents that exact format. A relative path resolves against the provider's
`secretPath`, a leading slash is absolute, and a ref without a colon keeps its
current meaning — the whole ref is the secret name at the provider's own path.

A dot cannot be the separator here: Infisical accepts dots inside secret names
(`A.B.C` is a valid key), so `provider.a.b.C` cannot be split unambiguously
and would silently break anyone using such a name.

Refs are grouped by resolved path so each path is listed once, and the login
happens once per batch rather than once per path.

Tests cover the bare ref, relative and absolute paths, a provider at `/`,
grouping with a single login, the error naming the path, and a malformed ref.

* fix(server-health): detect custom default-address-pools via docker info

/etc/docker/daemon.json isn't mounted into the dokploy container, only
docker.sock is, so cat-ing it always failed silently. Read the effective
config over the already-mounted socket instead.

Fixes Dokploy#5383

* Merge pull request Dokploy#5358 from KingIronMan2011/patch-1

feat: added missing Railpack versions
(cherry picked from commit 3269088)

[skip ci]

* Merge pull request Dokploy#5477 from Dokploy/feat/trial-expiration-emails

fix(billing): pick a plan for the trial and warn before it expires

(cherry picked from commit d7d75f7)

[skip ci]

* chore: release v0.30.7

* Merge pull request Dokploy#5538 from Dokploy/fix/hubspot-chat-widget

fix(cloud): load HubSpot chat widget for startup plan users

(cherry picked from commit d0ec74f)

[skip ci]

* chore: release v0.30.8

* Merge pull request Dokploy#5661 from manozzo/fix/template-card-tags-overflow

fix(templates): cap visible tags so the Create button stays visible

(cherry picked from commit d9fe046)

[skip ci]

* chore: release v0.30.9

* Merge pull request Dokploy#5738 from Dokploy/chore/bump-node-24.22.1

chore: bump Node.js to 24.22.1 (security release)
(cherry picked from commit eb98876)

[skip ci]

* chore: release v0.30.10

* chore: rebase retained fork patches onto Dokploy v0.30.10

* chore: preserve historical fork artifacts without running them

* fix: preserve Nano ID security floor during upstream rebase

* fix: scope provider credentials to the configured Git origin

---------

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: Mauricio Siu <[email protected]>
Co-authored-by: Mauricio Siu <[email protected]>
Co-authored-by: somuai <[email protected]>
Co-authored-by: Shuvo <[email protected]>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Narciso E. Núñez Arias <[email protected]>
Co-authored-by: Lucas Manchine <[email protected]>
Co-authored-by: drago1520 <[email protected]>
Co-authored-by: Romain PINSOLLE <[email protected]>
Co-authored-by: Shivam Gupta <[email protected]>
Co-authored-by: Rakesh Bhugra <[email protected]>
Co-authored-by: Dokploy Bot <[email protected]>
Co-authored-by: Yash Kumar <[email protected]>
Co-authored-by: Guillaume Juge <[email protected]>
Co-authored-by: Claude Opus 5 (1M context) <[email protected]>
Co-authored-by: Felix <[email protected]>
Co-authored-by: Bogdan Covrig <[email protected]>
Co-authored-by: Philippe Parage <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: detail-app[bot] <180357370+detail-app[bot]@users.noreply.github.com>
Co-authored-by: kcybe <[email protected]>
Co-authored-by: Artur Spatari <[email protected]>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request requested-changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: Add DNS provider support for Infomaniak

2 participants