diff --git a/CHANGELOG.md b/CHANGELOG.md
index 89975fad..69cd8607 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,7 @@
- **Client Configuration public key masked by default (#319):** Dashboard and Keys page show the KEY as bullets until revealed via an eye toggle; Copy still pastes the raw key. Ships via panel update.
### Fixed
+- **Native install TLS self-signed deploy (#325):** `_safe_cp_tls_file` no longer deletes `betterdesk.crt` when source and dest are the same real file (self-signed generated in place). Symlink→copy for Let's Encrypt (#219) is unchanged. Ships via installer / `betterdesk.sh` (not panel-only). Verify: `install.sh --native` completes past “Generating self-signed TLS certificates” with both `/opt/betterdesk/ssl/betterdesk.crt` and `.key` present.
- **Enrollment Requests UI (#320):** search icon no longer overlaps the placeholder; row dividers stay continuous under Platform/Actions (`display:flex` moved off `
`); Platform/Version/Status/Requested/Actions columns centered. Ships via panel update.
- **MeshAgent `bad web cert hash` behind reverse proxy (#321):** PEM-aware `WebCertHash` (first `CERTIFICATE` SPKI SHA-384) and optional `MESH_WEB_CERT_FILE` for the public TLS cert agents see (e.g. NPM Let's Encrypt), independent of Go `TLS_CERT`. Ships via panel update (Go restart). Verify: mount LE fullchain → set `MESH_WEB_CERT_FILE` → agent registers without web-hash mismatch.
- **Devices kebab Unban called Ban (#323):** kebab menu passed boolean `device.banned` while the handler only treated the string `'true'` as banned, so Unban opened the Ban modal and POSTed `/ban`. Both boolean and dataset string are accepted now. Ships via panel update.
diff --git a/betterdesk.sh b/betterdesk.sh
index 15f9db18..602d1c9d 100644
--- a/betterdesk.sh
+++ b/betterdesk.sh
@@ -880,8 +880,11 @@ resolve_le_cert_live_dir() {
echo "$le_live_dir"
}
-# Copy a TLS file to dest as a real file (not a symlink). Removes dest when it
-# already resolves to the same path as src — cp -L otherwise fails with "same file" (#219).
+# Copy a TLS file to dest as a real file (not a symlink).
+# - If dest is already the same real file as src (self-signed generated in place),
+# do nothing — deleting dest would remove src and break cp (#325).
+# - If dest is a symlink that resolves to src (LE live dir), remove the symlink
+# and copy content so the console user can read it (#219).
_safe_cp_tls_file() {
local src="$1"
local dest="$2"
@@ -892,7 +895,13 @@ _safe_cp_tls_file() {
if [ -e "$dest" ]; then
dest_real=$(readlink -f "$dest" 2>/dev/null || echo "$dest")
if [ "$src_real" = "$dest_real" ]; then
- rm -f "$dest"
+ if [ -L "$dest" ]; then
+ # Symlink to src → replace with a real copy (#219)
+ rm -f "$dest"
+ else
+ # Already a real file at dest (self-signed path) — no copy needed (#325)
+ return 0
+ fi
fi
fi
tmp="${dest}.betterdesk.$$.tmp"
|