web-agent-bridge 3.3.0 → 3.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/LICENSE +12 -0
  2. package/README.ar.md +18 -0
  3. package/README.md +198 -1664
  4. package/bin/wab-init.js +223 -0
  5. package/examples/azure-dns-wab.js +83 -0
  6. package/examples/cloudflare-wab-dns.js +121 -0
  7. package/examples/cpanel-wab-dns.js +114 -0
  8. package/examples/dns-discovery-agent.js +166 -0
  9. package/examples/gcp-dns-wab.js +76 -0
  10. package/examples/governance-agent.js +169 -0
  11. package/examples/plesk-wab-dns.js +103 -0
  12. package/examples/route53-wab-dns.js +144 -0
  13. package/examples/safe-mode-agent.js +96 -0
  14. package/examples/wab-sign.js +74 -0
  15. package/examples/wab-verify.js +60 -0
  16. package/package.json +5 -5
  17. package/public/.well-known/wab.json +28 -0
  18. package/public/activate.html +368 -0
  19. package/public/adoption-metrics.html +188 -0
  20. package/public/api.html +1 -1
  21. package/public/azure-dns-integration.html +289 -0
  22. package/public/cloudflare-integration.html +380 -0
  23. package/public/cpanel-integration.html +398 -0
  24. package/public/css/styles.css +28 -0
  25. package/public/dashboard.html +1 -0
  26. package/public/dns.html +101 -172
  27. package/public/docs.html +1 -0
  28. package/public/gcp-dns-integration.html +318 -0
  29. package/public/growth.html +4 -2
  30. package/public/index.html +227 -31
  31. package/public/integrations.html +1 -1
  32. package/public/js/activate.js +145 -0
  33. package/public/js/auth-nav.js +34 -0
  34. package/public/js/dns.js +438 -0
  35. package/public/openapi.json +89 -0
  36. package/public/plesk-integration.html +375 -0
  37. package/public/premium.html +1 -1
  38. package/public/provider-onboarding.html +172 -0
  39. package/public/provider-sandbox.html +134 -0
  40. package/public/providers.html +359 -0
  41. package/public/registrar-integrations.html +141 -0
  42. package/public/robots.txt +12 -0
  43. package/public/route53-integration.html +531 -0
  44. package/public/shieldqr.html +231 -0
  45. package/public/sitemap.xml +6 -0
  46. package/public/wab-trust.html +200 -0
  47. package/public/wab-vs-protocols.html +210 -0
  48. package/public/whitepaper.html +449 -0
  49. package/sdk/auto-discovery.js +288 -0
  50. package/sdk/governance.js +262 -0
  51. package/sdk/index.js +13 -0
  52. package/sdk/package.json +2 -2
  53. package/sdk/safe-mode.js +221 -0
  54. package/server/index.js +144 -5
  55. package/server/migrations/007_governance.sql +106 -0
  56. package/server/migrations/008_plans.sql +144 -0
  57. package/server/migrations/009_shieldqr.sql +30 -0
  58. package/server/migrations/010_extended_trust.sql +33 -0
  59. package/server/models/adapters/mysql.js +1 -1
  60. package/server/models/adapters/postgresql.js +1 -1
  61. package/server/models/db.js +60 -1
  62. package/server/routes/admin-plans.js +76 -0
  63. package/server/routes/admin-premium.js +4 -2
  64. package/server/routes/admin-shieldqr.js +90 -0
  65. package/server/routes/admin-trust-monitor.js +83 -0
  66. package/server/routes/admin.js +289 -1
  67. package/server/routes/billing.js +16 -4
  68. package/server/routes/discovery.js +1933 -2
  69. package/server/routes/governance.js +208 -0
  70. package/server/routes/plans.js +33 -0
  71. package/server/routes/providers.js +650 -0
  72. package/server/routes/shieldqr.js +88 -0
  73. package/server/services/email.js +29 -0
  74. package/server/services/governance.js +466 -0
  75. package/server/services/plans.js +214 -0
  76. package/server/services/premium.js +1 -1
  77. package/server/services/provider-clients.js +740 -0
  78. package/server/services/shieldqr.js +322 -0
  79. package/server/services/ssl-inspector.js +42 -0
  80. package/server/services/ssl-monitor.js +167 -0
  81. package/server/services/stripe.js +18 -5
  82. package/server/services/vision.js +1 -1
  83. package/server/services/wab-crypto.js +178 -0
package/public/dns.html CHANGED
@@ -12,10 +12,10 @@
12
12
  <link rel="stylesheet" href="/css/styles.css?v=3.2.0">
13
13
  <style>
14
14
  body{background:#070d19;color:#e5edff}
15
- .hero{padding:110px 24px 32px;text-align:center}
15
+ .hero{padding:120px 24px 32px !important;text-align:center;flex-direction:column;justify-content:center;align-items:center;min-height:auto !important}
16
16
  .hero h1{font-size:clamp(2rem,4vw,3rem);margin-bottom:10px}
17
17
  .hero p{max-width:880px;margin:0 auto;color:#99a8c7;line-height:1.7}
18
- .lang{display:flex;gap:10px;justify-content:center;margin:0 0 22px}
18
+ .lang{display:flex;gap:8px;align-items:center}
19
19
  .lang button{border:1px solid rgba(255,255,255,.15);background:rgba(255,255,255,.04);color:#e5edff;padding:8px 14px;border-radius:10px;cursor:pointer}
20
20
  .lang button.active{background:linear-gradient(135deg,#f59e0b,#b45309);border-color:transparent}
21
21
  .wrap{max-width:1100px;margin:0 auto;padding:0 22px 60px}
@@ -43,6 +43,12 @@
43
43
  .toc{display:flex;flex-wrap:wrap;gap:8px;justify-content:center;margin:0 0 14px}
44
44
  .toc a{background:rgba(255,255,255,.04);border:1px solid rgba(148,163,184,.22);color:#e5edff;padding:6px 12px;border-radius:999px;font-size:.85rem;text-decoration:none}
45
45
  .toc a:hover{border-color:#fbbf24;color:#fcd34d}
46
+ .advanced-block{display:none}
47
+ .live-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:12px;margin-top:12px}
48
+ .live-item{background:rgba(2,6,23,.6);border:1px solid rgba(148,163,184,.2);border-radius:12px;padding:12px}
49
+ .live-item h4{margin:0 0 6px;color:#fef3c7;font-size:.98rem}
50
+ .live-item p{margin:0 0 8px;color:#cbd5e1;font-size:.88rem;line-height:1.55}
51
+ .live-meta{font-size:.78rem;color:#93c5fd}
46
52
  </style>
47
53
  </head>
48
54
  <body>
@@ -57,20 +63,22 @@
57
63
  <li><a href="/sovereign">Sovereign</a></li>
58
64
  <li><a href="/docs">Docs</a></li>
59
65
  </ul>
66
+ <div class="lang" style="display:flex;gap:8px;align-items:center;margin-left:16px">
67
+ <button id="enBtn" class="active" style="border:1px solid rgba(255,255,255,.2);background:rgba(255,255,255,.06);color:#e5edff;padding:5px 12px;border-radius:8px;cursor:pointer;font-size:.82rem;font-weight:600">English</button>
68
+ <button id="arBtn" style="border:1px solid rgba(255,255,255,.2);background:rgba(255,255,255,.06);color:#e5edff;padding:5px 12px;border-radius:8px;cursor:pointer;font-size:.82rem;font-weight:600">العربية</button>
69
+ </div>
60
70
  </div>
61
71
  </nav>
62
72
 
63
73
  <section class="hero">
64
- <div class="lang">
65
- <button id="enBtn" class="active" onclick="setLang('en')">English</button>
66
- <button id="arBtn" onclick="setLang('ar')">العربية</button>
67
- </div>
68
74
  <h1 data-en="WAB DNS Discovery" data-ar="اكتشاف WAB عبر DNS">WAB DNS Discovery</h1>
69
75
  <p data-en="Announce your AI-readiness at the infrastructure level. Add a single DNS TXT record at _wab.yourdomain.com and any compatible AI agent will instantly find your wab.json capabilities — no HTTP probe required." data-ar="أعلن عن جاهزية موقعك للذكاء الاصطناعي على مستوى البنية التحتية. أضف سجل TXT واحد في _wab.yourdomain.com وسيجد أي وكيل ذكاء اصطناعي متوافق ملف القدرات wab.json فوراً — دون الحاجة لأي طلب HTTP أولي.">Announce your AI-readiness at the infrastructure level. Add a single DNS TXT record at <code>_wab.yourdomain.com</code> and any compatible AI agent will instantly find your <code>wab.json</code> capabilities — no HTTP probe required.</p>
70
76
  <div class="toc">
71
77
  <a href="#what-is">What & Why</a>
78
+ <a href="#quickstart">3 Steps</a>
72
79
  <a href="#format">Record Format</a>
73
80
  <a href="#verifier">Live Verifier</a>
81
+ <a href="#proof-lab">Proof Lab</a>
74
82
  <a href="#setup">Setup Guides</a>
75
83
  <a href="#troubleshoot">Troubleshooting</a>
76
84
  <a href="#repo-links">Repo & Docs</a>
@@ -93,6 +101,18 @@
93
101
  <span class="badge" data-en="MIT-licensed open protocol" data-ar="بروتوكول مفتوح برخصة MIT">MIT-licensed open protocol</span>
94
102
  </div>
95
103
 
104
+ <!-- ═══════════════ QUICKSTART ═══════════════ -->
105
+ <div class="card" id="quickstart">
106
+ <h3 data-en="The whole thing in 3 steps" data-ar="كل العملية في 3 خطوات">The whole thing in 3 steps</h3>
107
+ <ol>
108
+ <li data-en="Add TXT at _wab.yourdomain.com" data-ar="أضف TXT في _wab.yourdomain.com">Add TXT at <code>_wab.yourdomain.com</code></li>
109
+ <li data-en="Serve /.well-known/wab.json" data-ar="وفّر /.well-known/wab.json">Serve <code>/.well-known/wab.json</code></li>
110
+ <li data-en="Run Verify Live + Test with Agent" data-ar="شغّل Verify Live + Test with Agent">Run <b>Verify Live</b> + <b>Test with Agent</b></li>
111
+ </ol>
112
+ <p data-en="No appId required for protocol verification. DNS + wab.json are sufficient." data-ar="لا يلزم appId للتحقق البروتوكولي. DNS + wab.json كافيان.">No appId required for protocol verification. DNS + wab.json are sufficient.</p>
113
+ <button id="dnsAdvancedToggleBtn" class="btn secondary" style="margin-top:4px" data-en="Show Advanced Trust/Privacy Layer" data-ar="إظهار طبقة الثقة/الخصوصية المتقدمة">Show Advanced Trust/Privacy Layer</button>
114
+ </div>
115
+
96
116
  <!-- ═══════════════ HOW IT WORKS ═══════════════ -->
97
117
  <div class="card">
98
118
  <h3 data-en="How it works (under the hood)" data-ar="كيف يعمل (الآلية الداخلية)">How it works (under the hood)</h3>
@@ -197,15 +217,83 @@
197
217
  </select>
198
218
  </div>
199
219
  </div>
200
- <button class="btn" onclick="verifyDns()" data-en="Verify _wab Record" data-ar="تحقق من سجل _wab">Verify _wab Record</button>
201
- <button class="btn secondary" onclick="copyExample()" data-en="Copy Example Record" data-ar="انسخ سجلاً نموذجياً">Copy Example Record</button>
220
+ <button id="dnsVerifyBtn" class="btn" data-en="Verify _wab Record" data-ar="تحقق من سجل _wab">Verify _wab Record</button>
221
+ <button id="dnsCopyBtn" class="btn secondary" data-en="Copy Example Record" data-ar="انسخ سجلاً نموذجياً">Copy Example Record</button>
202
222
  <div id="dnsStatus" style="margin-top:10px;font-size:.92rem"></div>
203
223
  <pre id="dnsOut">{}</pre>
204
224
  </div>
205
225
  </div>
206
226
 
227
+ <!-- ═══════════════ PROOF LAB ═══════════════ -->
228
+ <div class="card" id="proof-lab">
229
+ <h3 data-en="Proof Lab — verify and execute live" data-ar="مختبر الإثبات — تحقق ونفّذ مباشرة">Proof Lab — verify and execute live</h3>
230
+ <p data-en="This section proves real activation and real usage. For each domain: DNS TXT, wab.json URL, live verify, and agent test flow (discover → ping)." data-ar="هذا القسم يثبت التفعيل الحقيقي والاستخدام الحقيقي. لكل نطاق: DNS TXT، رابط wab.json، تحقق مباشر، وتدفق اختبار الوكيل (discover → ping).">This section proves real activation and real usage.</p>
231
+ <div class="verifier">
232
+ <button id="dnsProofVerifyBtn" class="btn" data-en="Verify Live" data-ar="تحقق مباشر">Verify Live</button>
233
+ <button id="dnsProofAgentBtn" class="btn secondary" data-en="Test with Agent" data-ar="اختبر مع وكيل">Test with Agent</button>
234
+ <div style="margin-top:10px" class="row">
235
+ <div>
236
+ <label data-en="API Key (optional, for real execution proof)" data-ar="مفتاح API (اختياري لإثبات تنفيذ فعلي)">API Key (optional, for real execution proof)</label>
237
+ <input id="dnsUsageApiKey" type="password" placeholder="wab_xxxxx">
238
+ </div>
239
+ <div>
240
+ <label data-en="Preferred use-case" data-ar="الاستخدام المفضل">Preferred use-case</label>
241
+ <select id="dnsUsageUseCase">
242
+ <option value="">Auto</option>
243
+ <option value="booking">booking</option>
244
+ <option value="messaging">messaging</option>
245
+ <option value="payment">payment</option>
246
+ <option value="checkout">checkout</option>
247
+ <option value="search">search</option>
248
+ </select>
249
+ </div>
250
+ </div>
251
+ <button id="dnsUsageProofBtn" class="btn" data-en="Run Usage Proof" data-ar="شغّل إثبات الاستخدام">Run Usage Proof</button>
252
+ <a href="/downloads/wab-dns-provider.postman_collection.json" download class="btn secondary" style="margin-left:8px;font-size:.87rem;padding:6px 14px" data-en="Download Postman Collection" data-ar="تحميل مجموعة Postman">⬇ Download Postman Collection</a>
253
+ <div id="proofStatus" style="margin-top:10px;font-size:.92rem"></div>
254
+ <div id="proofDiscoverPathBadge" style="display:none;margin-top:8px"></div>
255
+ </div>
256
+ <div class="grid" style="grid-template-columns:repeat(auto-fit,minmax(220px,1fr));margin-top:10px">
257
+ <div class="card" style="padding:12px">
258
+ <h4 style="margin:0 0 6px">State</h4>
259
+ <div id="stateBadges" style="font-size:.9rem;color:#cbd5e1"></div>
260
+ </div>
261
+ <div class="card" style="padding:12px">
262
+ <h4 style="margin:0 0 6px">Use-case</h4>
263
+ <div id="useCaseValue" style="font-size:.9rem;color:#cbd5e1">—</div>
264
+ </div>
265
+ <div class="card" style="padding:12px">
266
+ <h4 style="margin:0 0 6px">DNS TXT</h4>
267
+ <div id="proofTxt" style="font-size:.84rem;color:#cbd5e1;word-break:break-word">—</div>
268
+ </div>
269
+ <div class="card" style="padding:12px">
270
+ <h4 style="margin:0 0 6px">wab.json</h4>
271
+ <div id="proofWabJson" style="font-size:.84rem;color:#cbd5e1;word-break:break-word">—</div>
272
+ </div>
273
+ </div>
274
+ <div class="card" style="padding:12px;margin-top:10px">
275
+ <h4 style="margin:0 0 6px">Usage KPI</h4>
276
+ <div id="usageKpiValue" style="font-size:.9rem;color:#cbd5e1">—</div>
277
+ </div>
278
+ <div class="card" style="padding:12px;margin-top:10px">
279
+ <h4 style="margin:0 0 6px">Usage Trend (Last 20 Runs)</h4>
280
+ <div id="usageTrendSummary" style="font-size:.9rem;color:#cbd5e1">—</div>
281
+ <div id="usageTrendList" style="margin-top:8px;font-size:.82rem;color:#aab6d3;line-height:1.6">—</div>
282
+ </div>
283
+ <pre id="proofOut">{}</pre>
284
+ </div>
285
+ </div>
286
+
287
+ <!-- ═══════════════ LIVE ADOPTION ═══════════════ -->
288
+ <div class="card" id="live-adoption">
289
+ <h3 data-en="Businesses Using WAB DNS Discovery (Live)" data-ar="الشركات التي تستخدم WAB DNS Discovery (مباشر)">Businesses Using WAB DNS Discovery (Live)</h3>
290
+ <p data-en="Auto-listed from real registrations only. No static or manually seeded entries." data-ar="قائمة تلقائية من التسجيلات الحقيقية فقط. لا توجد بيانات ثابتة أو مزروعة يدويًا.">Auto-listed from real registrations only. No static or manually seeded entries.</p>
291
+ <div id="liveAdoptionStatus" style="margin-top:8px;font-size:.9rem;color:#94a3b8">Loading live registry…</div>
292
+ <div id="liveAdoptionList" class="live-grid" style="display:none"></div>
293
+ </div>
294
+
207
295
  <!-- ═══════════════ PRIVACY MODEL ═══════════════ -->
208
- <div class="card" id="privacy">
296
+ <div class="card advanced-block" id="privacy">
209
297
  <h3 data-en="DNS Privacy Model — what DoH does and does not protect" data-ar="نموذج خصوصية DNS — ما الذي يحميه DoH وما لا يحميه">DNS Privacy Model — what DoH does and does not protect</h3>
210
298
  <p data-en="WAB Discovery never uses plain UDP DNS. Every lookup of _wab.example.com uses DNS over HTTPS (DoH), so the query is encrypted between the agent and the resolver. This is a real privacy upgrade — but it is not perfect, and we want you to understand the exact threat model." data-ar="لا يستخدم اكتشاف WAB أبداً DNS التقليدي عبر UDP. كل استعلام لـ _wab.example.com يتم عبر DNS over HTTPS (DoH)، فيكون مشفراً بين الوكيل والمحلل. هذه تحسين خصوصية حقيقي — لكنه ليس مثالياً، ونريد أن تفهم نموذج التهديد بدقة.">WAB Discovery never uses plain UDP DNS. Every lookup of <code>_wab.example.com</code> uses DNS over HTTPS (DoH), so the query is encrypted between the agent and the resolver.</p>
211
299
  <table class="table">
@@ -223,7 +311,7 @@
223
311
  </div>
224
312
 
225
313
  <!-- ═══════════════ SILENT HANDSHAKE ═══════════════ -->
226
- <div class="card" id="silent-handshake">
314
+ <div class="card advanced-block" id="silent-handshake">
227
315
  <h3 data-en="Silent Handshake — agent-aware sites, zero cookie banners" data-ar="المصافحة الصامتة — مواقع تفهم الوكلاء بدون نوافذ كوكيز">Silent Handshake — agent-aware sites, zero cookie banners</h3>
228
316
  <p data-en="When a WAB-aware site advertises _wab and _wab-trust over DNS, the visiting agent and the site can negotiate intent in the background — no privacy popup, no cookie banner, no consent click-fest. The site declares 'I respect your agent, here is the contract' once, in DNS, signed and cacheable." data-ar="عندما يعلن موقع متوافق مع WAB عن _wab و _wab-trust في DNS، يمكن للوكيل الزائر والموقع أن يتفاوضا في الخلفية — بدون نوافذ خصوصية ولا بانر كوكيز ولا نقرات موافقة. يعلن الموقع مرة واحدة في DNS: «أنا أحترم وكيلك، هذا هو العقد».">When a WAB-aware site advertises <code>_wab</code> and <code>_wab-trust</code> over DNS, the agent and site negotiate intent in the background — no privacy popup, no cookie banner, no consent click-fest.</p>
229
317
  <ol>
@@ -334,6 +422,7 @@ curl -s -H 'accept: application/dns-json' \
334
422
  <ul>
335
423
  <li><a href="https://github.com/abokenan444/web-agent-bridge/blob/master/DNS-DISCOVERY.md" target="_blank" rel="noopener">DNS-DISCOVERY.md (English)</a></li>
336
424
  <li><a href="https://github.com/abokenan444/web-agent-bridge/blob/master/DNS-DISCOVERY.ar.md" target="_blank" rel="noopener">DNS-DISCOVERY.ar.md (العربية)</a></li>
425
+ <li><a href="/provider-onboarding">Provider Onboarding (DNS providers & registrars)</a></li>
337
426
  <li><a href="https://github.com/abokenan444/web-agent-bridge/blob/master/docs/SPEC.md#4-discovery-protocol" target="_blank" rel="noopener">docs/SPEC.md — Discovery Protocol §4</a></li>
338
427
  <li><a href="/.well-known/wab.json" target="_blank">/.well-known/wab.json (this site's capabilities)</a></li>
339
428
  </ul>
@@ -341,167 +430,7 @@ curl -s -H 'accept: application/dns-json' \
341
430
 
342
431
  </div>
343
432
 
344
- <script>
345
- // ═══════════ Bilingual toggle ═══════════
346
- function setLang(lang){
347
- const isAr = lang === 'ar';
348
- document.documentElement.lang = isAr ? 'ar' : 'en';
349
- document.documentElement.dir = isAr ? 'rtl' : 'ltr';
350
- document.getElementById('enBtn').classList.toggle('active', !isAr);
351
- document.getElementById('arBtn').classList.toggle('active', isAr);
352
- document.querySelectorAll('[data-en]').forEach(el=>{
353
- el.textContent = isAr ? (el.getAttribute('data-ar') || el.textContent) : (el.getAttribute('data-en') || el.textContent);
354
- });
355
- }
356
-
357
- // ═══════════ Live DoH Verifier ═══════════
358
- async function verifyDns(){
359
- const domain = (document.getElementById('dnsDomain').value || '').trim().replace(/^https?:\/\//,'').replace(/\/.*$/,'');
360
- const resolver = document.getElementById('dnsResolver').value;
361
- const status = document.getElementById('dnsStatus');
362
- const out = document.getElementById('dnsOut');
363
- if(!domain){ status.innerHTML = '<span class="danger">Please enter a domain.</span>'; return; }
364
- const fqdn = '_wab.' + domain;
365
- status.innerHTML = '<span class="warn">Querying ' + fqdn + ' …</span>';
366
- out.textContent = '';
367
- try {
368
- const url = resolver + '?name=' + encodeURIComponent(fqdn) + '&type=TXT';
369
- const res = await fetch(url, { headers: { 'accept': 'application/dns-json' } });
370
- const data = await res.json();
371
- out.textContent = JSON.stringify(data, null, 2);
372
- const answers = (data.Answer || []).filter(a => a.type === 16);
373
- if (!answers.length) {
374
- status.innerHTML = '<span class="danger">No _wab TXT record found for <b>' + domain + '</b>. The domain has not enabled WAB DNS Discovery (yet).</span>';
375
- return;
376
- }
377
- const value = answers.map(a => (a.data || '').replace(/^"|"$/g,'').replace(/" "/g,'')).join(' ');
378
- const versionMatch = /v=([^;\s]+)/.exec(value);
379
- const endpointMatch = /endpoint=([^;\s]+)/.exec(value);
380
- if (versionMatch && endpointMatch && versionMatch[1].startsWith('wab')) {
381
- status.innerHTML = '<span class="ok">✓ Valid WAB record found. Version: <b>' + versionMatch[1] + '</b> · Endpoint: <a href="' + endpointMatch[1] + '" target="_blank" style="color:#7dd3fc">' + endpointMatch[1] + '</a></span>';
382
- } else {
383
- status.innerHTML = '<span class="warn">TXT record found but it does not match the WAB format (v=wab1; endpoint=…).</span>';
384
- }
385
- } catch (err) {
386
- status.innerHTML = '<span class="danger">Lookup failed: ' + (err && err.message || err) + '</span>';
387
- }
388
- }
389
-
390
- function copyExample(){
391
- const txt = 'v=wab1; endpoint=https://yourdomain.com/.well-known/wab.json';
392
- navigator.clipboard.writeText(txt).then(()=>{
393
- const s = document.getElementById('dnsStatus');
394
- s.innerHTML = '<span class="ok">✓ Example record copied to clipboard.</span>';
395
- });
396
- }
397
-
398
- // ═══════════ Canonical records — live status ═══════════
399
- const RR_TYPE = { TXT: 16, CAA: 257, A: 1, AAAA: 28, CNAME: 5 };
400
-
401
- function _decodeCAARdata(hex){
402
- // RFC 8659: 1 byte flags, 1 byte tag-length, tag, value (rest)
403
- try {
404
- const bytes = hex.replace(/\\#\s*\d+\s*/, '').replace(/\s+/g, '');
405
- const buf = bytes.match(/.{1,2}/g).map(b => parseInt(b, 16));
406
- const tagLen = buf[1];
407
- let tag = '', val = '';
408
- for (let i = 0; i < tagLen; i++) tag += String.fromCharCode(buf[2 + i]);
409
- for (let i = 2 + tagLen; i < buf.length; i++) val += String.fromCharCode(buf[i]);
410
- return { tag, value: val };
411
- } catch { return null; }
412
- }
413
-
414
- function _normalizeAnswer(answer, type){
415
- const data = answer.data || '';
416
- if (type === 'CAA') {
417
- const decoded = _decodeCAARdata(data);
418
- if (decoded) return decoded.tag + ' ' + decoded.value;
419
- // Some resolvers return the parsed form already (e.g. '0 issue "letsencrypt.org"')
420
- return data;
421
- }
422
- return data.replace(/^"|"$/g, '').replace(/"\s*"/g, '');
423
- }
424
-
425
- async function _doh(name, type){
426
- // do=1 asks Cloudflare to set the AD flag when the answer is DNSSEC-validated.
427
- const url = 'https://cloudflare-dns.com/dns-query?name=' +
428
- encodeURIComponent(name) + '&type=' + encodeURIComponent(type) + '&do=1';
429
- const res = await fetch(url, { headers: { 'accept': 'application/dns-json' } });
430
- const data = await res.json();
431
- const want = RR_TYPE[type];
432
- const answers = (data.Answer || []).filter(a => a.type === want)
433
- .map(a => _normalizeAnswer(a, type));
434
- // Attach AD flag (DNSSEC validated) as a non-enumerable property
435
- Object.defineProperty(answers, '_ad', { value: !!data.AD, enumerable: false });
436
- return answers;
437
- }
438
-
439
- async function checkDnssecForWab(){
440
- const el = document.getElementById('dnssecLiveStatus');
441
- const rowState = document.getElementById('dnssecRowState');
442
- if (!el) return;
443
- try {
444
- const ans = await _doh('_wab.webagentbridge.com', 'TXT');
445
- if (ans._ad) {
446
- el.className = 'ok';
447
- el.textContent = '✓ DNSSEC validated (AD=1) at resolver';
448
- if (rowState) { rowState.className = 'ok'; rowState.textContent = '✓ DNSSEC validated'; }
449
- } else {
450
- el.className = 'warn';
451
- el.textContent = '⚠ DNSSEC not yet enabled on this zone (AD=0). Roadmap: enable DS at registrar.';
452
- }
453
- } catch {
454
- el.className = 'warn';
455
- el.textContent = '… could not verify (DoH unreachable)';
456
- }
457
- }
458
-
459
- async function checkCanonicalRecords(){
460
- const rows = document.querySelectorAll('#recordsTable tr[data-record]');
461
- const summary = document.getElementById('recordsLiveStatus');
462
- let pass = 0, fail = 0;
463
- const tasks = Array.from(rows).map(async (row) => {
464
- const cell = row.querySelector('.live-cell');
465
- const name = row.dataset.record;
466
- const type = row.dataset.rtype;
467
- const match = row.dataset.match;
468
- try {
469
- const answers = await _doh(name, type);
470
- const hit = answers.some(a => a.toLowerCase().includes(match.toLowerCase()));
471
- if (hit) {
472
- cell.innerHTML = '<span class="ok" title="Verified live via Cloudflare DoH">✓ live</span>';
473
- pass++;
474
- } else {
475
- cell.innerHTML = '<span class="danger" title="Record not yet propagated or missing">✗ missing</span>';
476
- fail++;
477
- }
478
- } catch (err) {
479
- cell.innerHTML = '<span class="warn" title="Lookup failed">… error</span>';
480
- }
481
- });
482
- await Promise.allSettled(tasks);
483
- const total = pass + fail;
484
- summary.innerHTML = '<span class="' + (fail === 0 ? 'ok' : 'warn') + '">' +
485
- (fail === 0 ? '✓ All ' + total + ' canonical records verified live (Cloudflare DoH).'
486
- : '⚠ ' + pass + '/' + total + ' records live — ' + fail + ' missing or propagating.') + '</span>';
487
- }
488
-
489
- document.addEventListener('DOMContentLoaded', () => {
490
- // Don't block the page; run the live checks in the background.
491
- checkCanonicalRecords().catch(()=>{});
492
- checkDnssecForWab().catch(()=>{});
493
- });
494
- </script>
495
-
496
- <script>
497
- // navbar scroll background (consistent with other pages)
498
- const navbar = document.getElementById('navbar');
499
- window.addEventListener('scroll', () => {
500
- if (!navbar) return;
501
- navbar.style.background = window.scrollY > 50
502
- ? 'rgba(7, 13, 25, 0.92)'
503
- : 'rgba(7, 13, 25, 0.78)';
504
- });
505
- </script>
433
+
434
+ <script src="/js/dns.js"></script>
506
435
  </body>
507
436
  </html>
package/public/docs.html CHANGED
@@ -24,6 +24,7 @@
24
24
  <li><a href="/#features">Features</a></li>
25
25
  <li><a href="/#pricing">Pricing</a></li>
26
26
  <li><a href="/docs" style="color:var(--text-primary);">Docs</a></li>
27
+ <li><a href="/whitepaper">Whitepaper</a></li>
27
28
  </ul>
28
29
  <div class="navbar-actions">
29
30
  <a href="/login" class="btn btn-ghost" data-wab-auth="guest">Sign In</a>