Fix .conf downloads so WireGuard apps accept the import#11
Conversation
WireGuard for Android derives the tunnel name from the imported file's name and requires 1-15 chars of [a-zA-Z0-9_=+.-]. The Download .conf button used the raw account label + node name with no sanitization or length cap, so almost any real label (long, spaces, non-Latin) produced a file the app rejects with "Invalid name". Reuse the backend's confFilename rule (subscription.go) on the frontend: lowercase, collapse illegal chars to "-", truncate to 15, trim trailing separators. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
Code Review
This pull request introduces a confFilename helper function in AccountsPage.tsx to format and sanitize WireGuard configuration filenames, ensuring they conform to the 1-15 character limit and allowed character set. The reviewer pointed out that concatenating the account label and node name before truncating to 15 characters can cause collisions and subsequent browser renaming issues (which introduces invalid characters). They suggested updating confFilename to accept and truncate the label and node name individually, and provided code suggestions to implement this change.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| function confFilename(label: string): string { | ||
| let stem = label.toLowerCase().replace(/[^a-z0-9._-]+/g, '-') | ||
| if (stem.length > 15) stem = stem.slice(0, 15) | ||
| // Trim AFTER truncating so a cut landing on a separator doesn't leave a | ||
| // trailing "-"/"." right before the extension. | ||
| stem = stem.replace(/^[-.]+|[-.]+$/g, '') | ||
| return `${stem === '' ? 'wgpanel' : stem}.conf` | ||
| } |
There was a problem hiding this comment.
When downloading configs for multiple nodes under the same account, combining the account label and node name and then truncating the entire string to 15 characters can easily lead to identical filenames if the account label is long (e.g., verylongaccountlabel-node1 and verylongaccountlabel-node2 both truncate to verylongaccount).
This causes two major issues:
- The user cannot distinguish which file belongs to which node.
- Browsers will automatically append suffixes like
(1)to avoid overwriting (e.g.,verylongaccount (1).conf), which contains spaces and parentheses. These characters are invalid under WireGuard's strict[a-zA-Z0-9_=+.-]pattern, causing the WireGuard app to reject the file upon import.
To prevent this, we should truncate the account label and node name individually before joining them, ensuring the total length remains within 15 characters.
function confFilename(label: string, node?: string): string {
let stem = label.toLowerCase().replace(/[^a-z0-9._-]+/g, '-')
if (node) {
const nodeStem = node.toLowerCase().replace(/[^a-z0-9._-]+/g, '-')
// Budget 15 chars: 9 for label, 1 for '-', 5 for node
const labelPart = stem.slice(0, 9)
const nodePart = nodeStem.slice(0, 5)
stem = labelPart + '-' + nodePart
} else if (stem.length > 15) {
stem = stem.slice(0, 15)
}
// Trim AFTER truncating so a cut landing on a separator doesn't leave a
// trailing "-"/"." right before the extension.
stem = stem.replace(/^[-.]+|[-.]+$/g, '')
return (stem === '' ? 'wgpanel' : stem) + '.conf'
}
| const link = document.createElement('a') | ||
| link.href = url | ||
| link.download = `${account.label}-${slug}.conf` | ||
| link.download = confFilename(`${account.label}-${configNodeName ?? 'node'}`) |
There was a problem hiding this comment.
Pass the account label and node name as separate arguments to the updated confFilename function to ensure they are truncated individually and fit within the 15-character limit without collisions.
| link.download = confFilename(`${account.label}-${configNodeName ?? 'node'}`) | |
| link.download = confFilename(account.label, configNodeName ?? 'node') |
Summary
Two fixes for configs downloaded from the panel failing to import into the WireGuard app (Android):
[a-zA-Z0-9_=+.-](the wg-quick interface-name limit). The account dialog's Download .conf button named files<account label>-<node name>.confwith no sanitization or length cap, so almost any real label (long, spaces, Persian/non-Latin) produced a file the app rejects. The frontend now applies the same rule the backend's subscription endpoint already uses (confFilenameinbackend/internal/httpapi/subscription.go): lowercase, collapse illegal characters to-, truncate to 15 chars, trim trailing-/., fall back towgpanel..conf.txt— Android Chrome renamestext/plaindownloads whose extension it doesn't associate with that MIME type, appending.txt— and the WireGuard app refuses.conf.txt. Both download surfaces (the frontend blob download andGET /api/v1/sub/{token}'s attachment response) now serveapplication/octet-stream; the OpenAPI spec documents the change.Testing
go build ./...,gofmt -l, andnpx tsc --noEmitall pass..conffilenames within the 15-char charset import cleanly, and octet-stream downloads keep their.confextension on Android Chrome.🤖 Generated with Claude Code