Allow Email DNS updates with RSA-only DKIM paste.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Alireza Hassani
2026-09-18 14:26:05 +03:30
co-authored by Cursor
parent c344ddefdc
commit 8630465d34
2 changed files with 31 additions and 17 deletions
@@ -24,6 +24,12 @@ function keyReady(paste: string, selector: string) {
return pasteIncludesDomainkey(trimmed) || Boolean(selector.trim()) return pasteIncludesDomainkey(trimmed) || Boolean(selector.trim())
} }
/** Empty = skip Ed25519; otherwise same rules as RSA. */
function optionalKeyReady(paste: string, selector: string) {
if (!paste.trim()) return true
return keyReady(paste, selector)
}
export function EmailDnsModal({ domain, onClose }: EmailDnsModalProps) { export function EmailDnsModal({ domain, onClose }: EmailDnsModalProps) {
const { showToast } = useToast() const { showToast } = useToast()
const [dnsProvider, setDnsProvider] = useState<ParkedDnsProvider>('arvan') const [dnsProvider, setDnsProvider] = useState<ParkedDnsProvider>('arvan')
@@ -48,17 +54,19 @@ export function EmailDnsModal({ domain, onClose }: EmailDnsModalProps) {
if (!domain) return null if (!domain) return null
const target = domain const target = domain
const canSubmit = const canSubmit =
keyReady(ed25519Key, ed25519Selector) && keyReady(rsaKey, rsaSelector) keyReady(rsaKey, rsaSelector) &&
optionalKeyReady(ed25519Key, ed25519Selector)
async function submit() { async function submit() {
setSubmitting(true) setSubmitting(true)
setError('') setError('')
try { try {
const ed25519Paste = ed25519Key.trim()
const result = await applyMailDns(target.id, { const result = await applyMailDns(target.id, {
dnsProvider, dnsProvider,
ed25519Key,
rsaKey, rsaKey,
...(ed25519Selector.trim() ...(ed25519Paste ? { ed25519Key: ed25519Paste } : {}),
...(ed25519Paste && ed25519Selector.trim()
? { ed25519Selector: ed25519Selector.trim() } ? { ed25519Selector: ed25519Selector.trim() }
: {}), : {}),
...(rsaSelector.trim() ? { rsaSelector: rsaSelector.trim() } : {}), ...(rsaSelector.trim() ? { rsaSelector: rsaSelector.trim() } : {}),
@@ -85,14 +93,16 @@ export function EmailDnsModal({ domain, onClose }: EmailDnsModalProps) {
}} }}
> >
<p className={parkedStyles.hint}> <p className={parkedStyles.hint}>
Paste each DKIM <strong>TXT value</strong> (<code>v=DKIM1; …</code>) from Paste the RSA DKIM <strong>TXT value</strong> (<code>v=DKIM1; …</code>) from
Stalwart, or the full zone line. For value-only pastes, fill the selector Stalwart, or the full zone line. Ed25519 is optional — leave it empty to
(for example <code>v1-ed25519-20260901</code>). We upsert common mail skip. For value-only pastes, fill the selector (for example{' '}
records on {dnsProvider === 'cloudflare' ? 'Cloudflare' : 'Arvan'}{' '} <code>v1-rsa-20260901</code>). We upsert common mail records on{' '}
(DNS-only / not proxied): <strong>mail</strong> A → 185.214.101.41, MX → {dnsProvider === 'cloudflare' ? 'Cloudflare' : 'Arvan'} (DNS-only / not
mail.meshkee.com (priority 10), SPF <code>v=spf1 mx -all</code>, both proxied): <strong>mail</strong> A → 185.214.101.41, MX → mail.meshkee.com
DKIM keys, autoconfig / autodiscover → mail.meshkee.com. Extra MX,{' '} (priority 10), SPF <code>v=spf1 mx -all</code>, DKIM (RSA required,
<strong>dkim._domainkey</strong>, and <strong>_dmarc</strong> are removed. Ed25519 if provided), autoconfig / autodiscover → mail.meshkee.com. Extra
MX, <strong>dkim._domainkey</strong>, and <strong>_dmarc</strong> are
removed.
</p> </p>
{error ? ( {error ? (
<p className={tableStyles.alertError} role="alert"> <p className={tableStyles.alertError} role="alert">
@@ -115,7 +125,9 @@ export function EmailDnsModal({ domain, onClose }: EmailDnsModalProps) {
</div> </div>
<div className={tableStyles.field}> <div className={tableStyles.field}>
<label htmlFor="mail-dkim-ed25519-selector">Ed25519 selector</label> <label htmlFor="mail-dkim-ed25519-selector">
Ed25519 selector <span className={tableStyles.helperText}>(optional)</span>
</label>
<input <input
id="mail-dkim-ed25519-selector" id="mail-dkim-ed25519-selector"
className={styles.selectorInput} className={styles.selectorInput}
@@ -129,18 +141,20 @@ export function EmailDnsModal({ domain, onClose }: EmailDnsModalProps) {
autoComplete="off" autoComplete="off"
/> />
<p className={tableStyles.helperText}> <p className={tableStyles.helperText}>
Needed when pasting only the TXT value. Optional if the paste includes{' '} Only needed with an Ed25519 paste that has no <code>…._domainkey</code>.
<code>…._domainkey</code>.
</p> </p>
</div> </div>
<div className={tableStyles.field}> <div className={tableStyles.field}>
<label htmlFor="mail-dkim-ed25519">Ed25519 domain key</label> <label htmlFor="mail-dkim-ed25519">
Ed25519 domain key{' '}
<span className={tableStyles.helperText}>(optional)</span>
</label>
<textarea <textarea
id="mail-dkim-ed25519" id="mail-dkim-ed25519"
className={styles.textarea} className={styles.textarea}
value={ed25519Key} value={ed25519Key}
onChange={(e) => setEd25519Key(e.target.value)} onChange={(e) => setEd25519Key(e.target.value)}
placeholder='v=DKIM1; k=ed25519; h=sha256; p=…' placeholder="Leave empty to skip — or paste v=DKIM1; k=ed25519; …"
disabled={submitting} disabled={submitting}
spellCheck={false} spellCheck={false}
dir="ltr" dir="ltr"
@@ -112,8 +112,8 @@ export async function applyMailDns(
domainId: number | string, domainId: number | string,
payload: { payload: {
dnsProvider: 'arvan' | 'cloudflare' dnsProvider: 'arvan' | 'cloudflare'
ed25519Key: string
rsaKey: string rsaKey: string
ed25519Key?: string
ed25519Selector?: string ed25519Selector?: string
rsaSelector?: string rsaSelector?: string
}, },