Choose which IPFS gateway to use for launching apps from CIDs. URLs will be constructed using subdomain gateway resolution https://{cid}.ipfs.gateway.com
- When enabled, the extension will try to use your local IPFS gateway first if available.
+ When enabled, the extension will try to use your local IPFS gateway first if available.
+ DNS over HTTPS URL is used for DNSLink resolution. Common providers: Cloudflare (cloudflare-dns.com/dns-query), Google (dns.google/dns-query), Quad9 (dns.quad9.net/dns-query).
diff --git a/src/popup.ts b/src/popup.ts
index 273e415..6216ec5 100644
--- a/src/popup.ts
+++ b/src/popup.ts
@@ -55,6 +55,7 @@ class PopupManager {
const cancelEditButton = document.getElementById('cancelEditButton');
const editAppForm = document.getElementById('editAppForm') as HTMLFormElement;
const preferLocalGateway = document.getElementById('preferLocalGateway') as HTMLInputElement;
+ const saveDnsOverHttps = document.getElementById('saveDnsOverHttps');
addButton?.addEventListener('click', () => this.showAddModal());
searchBox?.addEventListener('input', (e) => this.handleSearch((e.target as HTMLInputElement).value));
@@ -73,6 +74,7 @@ class PopupManager {
cancelEditButton?.addEventListener('click', () => this.hideEditModal());
editAppForm?.addEventListener('submit', (e) => this.handleEditApp(e));
preferLocalGateway?.addEventListener('change', (e) => this.handleLocalGatewayToggle((e.target as HTMLInputElement).checked));
+ saveDnsOverHttps?.addEventListener('click', () => this.handleSaveDnsOverHttps());
// Close modal when clicking outside
addAppModal?.addEventListener('click', (e) => {
@@ -297,8 +299,9 @@ class PopupManager {
const themeSelect = document.getElementById('themeSelect') as HTMLSelectElement;
const gatewaySelect = document.getElementById('gatewaySelect') as HTMLSelectElement;
const preferLocalGateway = document.getElementById('preferLocalGateway') as HTMLInputElement;
+ const dnsOverHttpsInput = document.getElementById('dnsOverHttpsInput') as HTMLInputElement;
- if (settingsModal && themeSelect && gatewaySelect && preferLocalGateway) {
+ if (settingsModal && themeSelect && gatewaySelect && preferLocalGateway && dnsOverHttpsInput) {
// Update theme select to current value
themeSelect.value = themeManager.getTheme();
@@ -310,6 +313,9 @@ class PopupManager {
// Update local gateway preference
preferLocalGateway.checked = gatewayConfig.preferLocalGateway || false;
+ // Update DNS over HTTPS URL
+ dnsOverHttpsInput.value = gatewayConfig.dnsOverHttpsUrl;
+
// Check if current gateway is in the predefined options
const predefinedOptions = ['dweb.link', 'inbrowser.link', 'inbrowser.dev'];
@@ -506,6 +512,33 @@ class PopupManager {
}
}
+ private async handleSaveDnsOverHttps(): Promise {
+ const dnsOverHttpsInput = document.getElementById('dnsOverHttpsInput') as HTMLInputElement;
+ const dnsOverHttpsUrl = dnsOverHttpsInput.value.trim();
+
+ if (!dnsOverHttpsUrl) {
+ this.showTemporaryMessage('Please enter a DNS over HTTPS URL', 'error');
+ return;
+ }
+
+ // Validate URL format
+ try {
+ const url = new URL(dnsOverHttpsUrl);
+
+ // Basic validation - should be HTTPS and have dns-query path or similar
+ if (url.protocol !== 'https:') {
+ this.showTemporaryMessage('DNS over HTTPS URL must use HTTPS', 'error');
+ return;
+ }
+
+ await storage.updateGatewayConfig({ dnsOverHttpsUrl });
+ this.showTemporaryMessage('DNS over HTTPS URL updated successfully!', 'success');
+ } catch (error) {
+ console.error('Failed to save DNS over HTTPS URL:', error);
+ this.showTemporaryMessage('Please enter a valid HTTPS URL', 'error');
+ }
+ }
+
private async handleExport(): Promise {
try {
await ExportManager.exportData();
diff --git a/src/storage/index.ts b/src/storage/index.ts
index 2538891..f14c970 100644
--- a/src/storage/index.ts
+++ b/src/storage/index.ts
@@ -2,6 +2,7 @@ import { App, AppStorage, CreateAppRequest, CreateVersionRequest, UpdateAppReque
import { LocalGatewayProbe } from '../utils/localGateway.js';
const STORAGE_KEY = 'ipfs_spark_data';
+const DEFAULT_DNS_OVER_HTTPS_URL = 'https://cloudflare-dns.com/dns-query';
export class StorageManager {
private static instance: StorageManager;
@@ -57,7 +58,8 @@ export class StorageManager {
'inbrowser.dev'
],
preferLocalGateway: false,
- localGatewayUrl: 'http://localhost:8080'
+ localGatewayUrl: 'http://localhost:8080',
+ dnsOverHttpsUrl: DEFAULT_DNS_OVER_HTTPS_URL
},
dnslinkCache: {}
};
@@ -181,7 +183,14 @@ export class StorageManager {
async getGatewayConfig(): Promise {
const data = this.cache || await this.load();
- return data.gatewayConfig;
+ const config = data.gatewayConfig;
+
+ // Ensure dnsOverHttpsUrl has a default value for existing installations
+ if (!config.dnsOverHttpsUrl) {
+ config.dnsOverHttpsUrl = DEFAULT_DNS_OVER_HTTPS_URL;
+ }
+
+ return config;
}
async updateGatewayConfig(config: Partial): Promise {
diff --git a/src/types/index.ts b/src/types/index.ts
index 3bff903..f3d2bdd 100644
--- a/src/types/index.ts
+++ b/src/types/index.ts
@@ -37,6 +37,7 @@ export interface GatewayConfig {
customGateways: string[];
preferLocalGateway: boolean;
localGatewayUrl: string;
+ dnsOverHttpsUrl: string;
}
export interface UserSettings {
diff --git a/src/utils/dnslink.ts b/src/utils/dnslink.ts
index 840857d..e7f5d67 100644
--- a/src/utils/dnslink.ts
+++ b/src/utils/dnslink.ts
@@ -8,25 +8,24 @@ export interface DNSLinkResult {
}
export class DNSLinkProbe {
- private static readonly DNS_OVER_HTTPS_URL = 'https://cloudflare-dns.com/dns-query';
private static readonly DNSLINK_PREFIX = 'dnslink=';
private static readonly IPFS_PREFIX = '/ipfs/';
/**
* Probe a domain for DNSLink records and x-ipfs-path headers
*/
- static async probe(domain: string): Promise {
+ static async probe(domain: string, dnsOverHttpsUrl: string): Promise {
try {
const cleanDomain = this.cleanDomain(domain);
// Try DNSLink first (preferred method)
// Try _dnslink subdomain first (RFC standard)
const dnslinkDomain = `_dnslink.${cleanDomain}`;
- let result = await this.queryTxtRecord(dnslinkDomain);
+ let result = await this.queryTxtRecord(dnslinkDomain, dnsOverHttpsUrl);
// If no _dnslink record, try the domain itself
if (!result.hasDNSLink) {
- result = await this.queryTxtRecord(cleanDomain);
+ result = await this.queryTxtRecord(cleanDomain, dnsOverHttpsUrl);
}
// If DNSLink found, return it
@@ -62,8 +61,8 @@ export class DNSLinkProbe {
/**
* Query TXT records for a domain using DNS over HTTPS
*/
- private static async queryTxtRecord(domain: string): Promise {
- const url = `${this.DNS_OVER_HTTPS_URL}?name=${encodeURIComponent(domain)}&type=TXT`;
+ private static async queryTxtRecord(domain: string, dnsOverHttpsUrl: string): Promise {
+ const url = `${dnsOverHttpsUrl}?name=${encodeURIComponent(domain)}&type=TXT`;
try {
const response = await fetch(url, {