From f60269c82461abe992202853b995352e67537af4 Mon Sep 17 00:00:00 2001 From: farhadzand Date: Sat, 18 Jul 2026 16:44:47 +0330 Subject: [PATCH 1/3] Serve .conf downloads as octet-stream so Android keeps the extension Android Chrome renames text/plain downloads whose extension it doesn't associate with that MIME type, so "x.conf" landed in Downloads as "x.conf.txt" - which the WireGuard app refuses to import. Switch both download surfaces (the frontend Download .conf blob and the subscription endpoint's attachment response) to application/octet-stream; the OpenAPI spec documents the change. Co-Authored-By: Claude Opus 4.8 --- backend/internal/httpapi/subscription.go | 6 +++++- docs/openapi.yaml | 8 ++++++-- frontend/src/pages/AccountsPage.tsx | 6 +++++- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/backend/internal/httpapi/subscription.go b/backend/internal/httpapi/subscription.go index 1675538..100f211 100644 --- a/backend/internal/httpapi/subscription.go +++ b/backend/internal/httpapi/subscription.go @@ -150,7 +150,11 @@ func (s *Server) handleSubscriptionConfig(w http.ResponseWriter, r *http.Request return } - w.Header().Set("Content-Type", "text/plain; charset=utf-8") + // application/octet-stream, NOT text/plain: this endpoint is downloaded straight + // from mobile browsers, and Android Chrome renames text/plain attachments whose + // extension it doesn't associate with that type - "x.conf" would land in Downloads + // as "x.conf.txt", which the WireGuard app refuses to import. + w.Header().Set("Content-Type", "application/octet-stream") w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%q", confFilename(account.Label))) w.WriteHeader(http.StatusOK) _, _ = w.Write([]byte(config)) diff --git a/docs/openapi.yaml b/docs/openapi.yaml index 4b0e58c..bf55b24 100644 --- a/docs/openapi.yaml +++ b/docs/openapi.yaml @@ -951,9 +951,13 @@ paths: schema: { type: string } responses: "200": - description: wg-quick config text (Content-Disposition attachment). + description: >- + wg-quick config text (Content-Disposition attachment). Served as + `application/octet-stream` rather than `text/plain` so Android + Chrome keeps the `.conf` extension instead of renaming the + download to `.conf.txt`, which the WireGuard app can't import. content: - text/plain: + application/octet-stream: schema: { type: string } "403": description: "`account_suspended`" diff --git a/frontend/src/pages/AccountsPage.tsx b/frontend/src/pages/AccountsPage.tsx index 185416f..02b3b84 100644 --- a/frontend/src/pages/AccountsPage.tsx +++ b/frontend/src/pages/AccountsPage.tsx @@ -789,7 +789,11 @@ function AccountDetailDialog({ + {token.panel_addr && ( +
+

+ When the installer asks for the control plane address, + enter exactly this — it is the panel's node-agent port, not the web UI address: +

+
+ + {token.panel_addr} + + +
+
+ )} )}