diff --git a/manifest.json b/manifest.json
index 22f371e..24ccff6 100644
--- a/manifest.json
+++ b/manifest.json
@@ -7,6 +7,10 @@
     "default_title": "Archive.org Link Grabber",
     "default_popup": "src/popup/index.html"
   },
+  "options_ui": {
+    "page": "src/options/index.html",
+    "open_in_tab": true
+  },
   "permissions": [
     "storage",
     "clipboardWrite",
@@ -28,4 +32,3 @@
     }
   ]
 }
-
diff --git a/src/lib/aria2.js b/src/lib/aria2.js
index 7509b4d..179881d 100644
--- a/src/lib/aria2.js
+++ b/src/lib/aria2.js
@@ -34,3 +34,21 @@ export async function addUrisBatch({ endpoint, secret, uris, options = {} }) {
   return results;
 }
 
+export async function getVersion({ endpoint, secret }) {
+  const body = {
+    jsonrpc: "2.0",
+    id: "archive-org-link-grabber:getVersion:" + Date.now(),
+    method: "aria2.getVersion",
+    params: [secret ? `token:${secret}` : undefined].filter(Boolean)
+  };
+  const res = await fetch(endpoint, {
+    method: 'POST',
+    headers: { 'Content-Type': 'application/json' },
+    body: JSON.stringify(body)
+  });
+  if (!res.ok) {
+    const text = await res.text().catch(() => String(res.status));
+    throw new Error(`aria2 RPC error ${res.status}: ${text}`);
+  }
+  return res.json();
+}
diff --git a/src/options/index.html b/src/options/index.html
new file mode 100644
index 0000000..3b01b46
--- /dev/null
+++ b/src/options/index.html
@@ -0,0 +1,34 @@
+
+
+  
+    
+    Archive.org Link Grabber — Options
+    
+  
+  
+    
+    
+      
+    
+    
+  
+  
+
diff --git a/src/options/index.js b/src/options/index.js
new file mode 100644
index 0000000..c39d152
--- /dev/null
+++ b/src/options/index.js
@@ -0,0 +1,52 @@
+import { getVersion } from "../lib/aria2.js";
+
+const els = {
+  endpoint: document.getElementById('rpc-endpoint'),
+  secret: document.getElementById('rpc-secret'),
+  btnSave: document.getElementById('btn-save'),
+  btnReset: document.getElementById('btn-reset'),
+  btnTest: document.getElementById('btn-test'),
+  status: document.getElementById('status'),
+};
+
+async function restore() {
+  const { aria2 } = await browser.storage.local.get('aria2');
+  els.endpoint.value = aria2?.endpoint || 'http://localhost:6800/jsonrpc';
+  els.secret.value = aria2?.secret || '';
+}
+
+async function save() {
+  const endpoint = els.endpoint.value.trim();
+  const secret = els.secret.value.trim();
+  await browser.storage.local.set({ aria2: { endpoint, secret } });
+  els.status.textContent = 'Saved.';
+}
+
+async function reset() {
+  await browser.storage.local.remove('aria2');
+  await restore();
+  els.status.textContent = 'Reset to defaults.';
+}
+
+async function testConnection() {
+  els.status.textContent = 'Testing…';
+  try {
+    const res = await getVersion({ endpoint: els.endpoint.value.trim(), secret: els.secret.value.trim() });
+    const version = res?.result?.version || JSON.stringify(res?.result || res);
+    els.status.textContent = `OK: aria2 version ${version}`;
+  } catch (e) {
+    els.status.textContent = `Error: ${e?.message || e}`;
+  }
+}
+
+function wire() {
+  els.btnSave.addEventListener('click', save);
+  els.btnReset.addEventListener('click', reset);
+  els.btnTest.addEventListener('click', testConnection);
+}
+
+document.addEventListener('DOMContentLoaded', async () => {
+  wire();
+  await restore();
+});
+
diff --git a/src/options/styles.css b/src/options/styles.css
new file mode 100644
index 0000000..722729e
--- /dev/null
+++ b/src/options/styles.css
@@ -0,0 +1,9 @@
+body { font-family: system-ui, -apple-system, Segoe UI, Roboto, sans-serif; margin: 16px; }
+header h1 { margin: 0 0 12px; font-size: 18px; }
+section { margin: 12px 0; }
+.row { display: flex; align-items: center; gap: 8px; margin: 8px 0; flex-wrap: wrap; }
+label { font-size: 14px; }
+label input { margin-left: 6px; min-width: 360px; }
+button { font-size: 13px; padding: 6px 12px; }
+#status { font-size: 13px; white-space: pre-wrap; }
+