From 0e5bc7dc24c40f3ffb8a148f087a12f20b06e0b0 Mon Sep 17 00:00:00 2001 From: Elmehdi Aitbrahim Date: Fri, 21 Aug 2026 21:07:35 -0400 Subject: [PATCH 1/2] =?UTF-8?q?feat(compliance):=20the=20discussion-#472?= =?UTF-8?q?=20primer=20=E2=80=94=20what=20the=20framework=20asks,=20three?= =?UTF-8?q?=20kinds=20of=20rules,=20and=20the=20'Shariah=20law'=20FAQ?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #70. Discussion keel#472 was the best usability test this page ever had: a non-technical US reader arrived asking what Shariah governance asks of money, whether 'Shariah law' should worry him, and whether his interest-paying bank blocks him. The answers that worked are already written — in the maintainer's replies — so the page now carries them, adapted faithfully: - What the framework asks of money: four prohibitions and one obligation (riba, gharar, maysir, prohibited sectors, zakat), ending on 'more conservative than a standard brokerage, not less'. - Three kinds of rules — and where your bank fits: what you supply (rulings as inputs, not a fatwa engine), what the engine enforces mechanically (settled-balance-only, never a linked bank, silence is not consent to spend), and what it cannot verify plus the remedy (USDC Rewards: runbook manual step, no fake config flag, purification ledger counts and reports). - 'Is this Shariah law?': the scare-tactic framing answered — self-constraint, not imposition; no embedded ruling; and the usury prohibition's thousand-year Christian history ('a road not taken'). EN/AR/FR with rev bumps; the engine-side question this discussion raised (sizing equity vs accrued reward income) is filed separately as keel#490 and deliberately not worked here. --- lh.mjs | 21 ++++++ src/components/pages/CompliancePage.astro | 5 ++ src/i18n/pages/compliance.ts | 83 +++++++++++++++++++++-- 3 files changed, 104 insertions(+), 5 deletions(-) create mode 100644 lh.mjs diff --git a/lh.mjs b/lh.mjs new file mode 100644 index 0000000..82a905d --- /dev/null +++ b/lh.mjs @@ -0,0 +1,21 @@ +import puppeteer from "puppeteer-core"; +import { spawnSync } from "node:child_process"; + +const browser = await puppeteer.launch({ + executablePath: "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome", + defaultViewport: null, + args: ["--remote-debugging-port=9222"], +}); +const pages = ["/en/", "/en/compare/", "/en/news/", "/ar/"]; +for (const p of pages) { + const r = spawnSync("npx", ["lighthouse", `https://keeltrading.com${p}`, "--port=9222", "--quiet", "--chrome-flags=--headless=new", "--only-categories=performance,accessibility,best-practices,seo", "--output=json", "--output-path=stdout"], { encoding: "utf8", maxBuffer: 64 * 1024 * 1024 }); + try { + const report = JSON.parse(r.stdout); + const cats = Object.fromEntries(Object.entries(report.categories).map(([k, v]) => [k, Math.round(v.score * 100)])); + const metrics = report.audits; + console.log(p, JSON.stringify({ ...cats, LCP: metrics["largest-contentful-paint"]?.displayValue, CLS: metrics["cumulative-layout-shift"]?.displayValue })); + } catch { + console.log(p, "PARSE-FAIL", (r.stdout || r.stderr || "").slice(0, 200)); + } +} +await browser.close(); diff --git a/src/components/pages/CompliancePage.astro b/src/components/pages/CompliancePage.astro index 89c6035..8f44c0e 100644 --- a/src/components/pages/CompliancePage.astro +++ b/src/components/pages/CompliancePage.astro @@ -66,6 +66,11 @@ const honestLinks = [ +
+

{c.framing.title}

+ {c.framing.body.map((paragraph) =>

{paragraph}

)} +
+
diff --git a/src/i18n/pages/compliance.ts b/src/i18n/pages/compliance.ts index b39519f..71fb8eb 100644 --- a/src/i18n/pages/compliance.ts +++ b/src/i18n/pages/compliance.ts @@ -13,6 +13,7 @@ export interface ComplianceContent { intro: string; sections: { title: string; body: string[] }[]; doesNot: { title: string; items: string[] }; + framing: { title: string; body: string[] }; attest: { title: string; body: string[]; command: string; commandNote: string }; links: { title: string; items: { label: string; note: string; href: string }[] }; translatedFromRev?: string; @@ -20,13 +21,20 @@ export interface ComplianceContent { export const compliance: LocalizedPage = { en: { - rev: "2026-08-20.2", + rev: "2026-08-22.1", title: "Shariah Compliance Methodology, in Plain Terms", description: "How keel's Shariah compliance works: fail-closed attested screening, the per-(venue, product) attestation model, the qabd check — and what keel is not.", intro: "keel's compliance is designed so that every ruling it enforces is written down, sourced, and supplied by you — the operator. Nothing religious is inferred, defaulted, or hidden in code. This page explains that model in plain language; the fiqh basis document is the authoritative statement, ruling by ruling.", sections: [ + { + title: "What the framework asks of money", + body: [ + "Shariah governance of finance comes down to four prohibitions and one obligation. Riba — interest: money cannot earn money by itself, and a return has to come from bearing real risk in real activity, not from lending at a guaranteed rate. Gharar — excessive uncertainty: contracts must be clear, and you cannot sell what you do not own or have not taken possession of. Maysir — gambling: no zero-sum wagering presented as investment. Prohibited sectors — alcohol, pork, gambling, the adult industry, weapons, conventional interest-based finance. And zakat, an annual levy of about 2.5% on idle wealth — an obligation to redistribute rather than a restriction.", + "What survives is unremarkable: own real assets, take genuine risk for genuine return. No leverage, no shorting, no derivatives, no selling what you do not hold. In practice this makes keel more conservative than a standard brokerage account, not less — it rules out most of the instruments that failed in 2008.", + ], + }, { title: "Computed facts vs attested classifications", body: [ @@ -40,6 +48,15 @@ export const compliance: LocalizedPage = { "The same model covers the venue itself. Rail 14 refuses live BUYs until the operator attests the venue subscription. Rail 17 encodes §65.4 qabd, the doctrine of constructive possession: an asset that cannot be withdrawn may never have been validly possessed. So withdrawal capability is attested and enforced, not assumed.", ], }, + { + title: "Three kinds of rules — and where your bank fits", + body: [ + "Can you use keel if your bank pays or charges interest? Yes — keel never touches your bank. It operates on one trading account and has no connection to, visibility of, or authority over anything your bank does. The sharper question is where each rule lives, and there are exactly three places:", + "What you supply: the classifications — is this asset's sector permissible, is it an owned thing ('ayn) or a claim on an issuer (dayn), does it pay a yield. The ruling is an input you record; keel is not a fatwa engine and never derives one.", + "What the engine enforces mechanically: these are invariants, not opinions. A buy may only spend an already-settled balance already sitting in the trading account — never a linked bank — and a balance the engine cannot read is a trade refused rather than attempted. Silence is not consent to spend.", + "What it cannot verify, and the remedy: a venue may pay interest inside the account itself — Coinbase's USDC Rewards on idle balances. The trading API exposes no rewards switch, so this cannot be automated, and a config flag claiming \"rewards disabled\" would be a comforting lie, so there isn't one. It is a required manual step in the operator runbook. When interest does accrue, it is counted: the purification ledger reports the amount that must be given away rather than kept. Moving the money is the operator's act, never the software's.", + ], + }, { title: "Purification and the spot-only shape", body: [ @@ -57,6 +74,14 @@ export const compliance: LocalizedPage = { "It does not promise profit. No shipped rule family is net positive at the taker fee actually paid, and the reference benchmark is simple DCA.", ], }, + framing: { + title: "Is this “Shariah law”?", + body: [ + "The phrase is used as a scare tactic in some places, and the word doing the damage is law — which implies something imposed on other people. keel is the opposite: one person constraining his own trading, using software that refuses to let him make a convenient exception later. Nobody's conduct is governed but the operator's own.", + "No ruling is embedded anywhere in the software. Two operators following different schools of thought get different results from identical code, because the ruling is an input you record, not an output you receive. The honest description is a compliance engine that happens to have been built for one ethical framework.", + "For perspective: the usury prohibition was Christian doctrine for roughly a thousand years — it runs through Aquinas, the Third Lateran Council, and centuries of canon law, and the same rule appears in Deuteronomy. Islam retained a prohibition Europe relaxed for commercial rather than theological reasons. It is not a foreign import; it is a road not taken.", + ], + }, attest: { title: "The command that matters", body: [ @@ -93,14 +118,21 @@ export const compliance: LocalizedPage = { }, ar: { - rev: "2026-08-20.2", - translatedFromRev: "2026-08-20.2", + rev: "2026-08-22.1", + translatedFromRev: "2026-08-22.1", title: "منهجية الامتثال الشرعي بلغةٍ مبسّطة", description: "كيف يعمل الامتثال الشرعي في كيل: فرزٌ موثَّق يرفض عند الفشل، ونموذجُ توثيقٍ لكل (منصّة، منتج)، وفحصُ القبض — وما لا يزعمه كيل.", intro: "صُمِّم الامتثال في كيل بحيث يكون كلُّ حكمٍ يُنفّذه مكتوبًا وموثَّق المصدر ومزوَّدًا منك أنت — المشغّل. فلا يُستنبَط شيءٌ شرعي، ولا يُفترض افتراضًا، ولا يُخفى في الشيفرة. وتشرح هذه الصفحة هذا النموذج بلغةٍ مبسّطة؛ ومستندُ الأساس الفقهي هو البيان المرجعي، حكمًا حكمًا.", sections: [ + { + title: "ما يطلبه الإطار من المال", + body: [ + "تنحصر الحوكمة الشرعية للمال في أربع حظراتٍ وواجبٍ واحد. الربا — الفائدة: لا يكسب المالُ مالًا بنفسه، ويجب أن يأتي العائدُ من تحمّل مخاطرةٍ حقيقيةٍ في نشاطٍ حقيقي، لا من الإقراض بمعدلٍ مضمون. الغرر — الجهالة المفرطة: العقودُ يجب أن تكون واضحة، ولا يجوز بيعُ ما لا تملك أو لم تقبضه. الميسر — القمار: لا رهانٍ بمجموعٍ صفريٍّ يُقدَّم بوصفه استثمارًا. القطاعات المحظورة — الخمور ولحم الخنزير والقمار والصناعة الإباحية والأسلحة والمالية الربوية التقليدية. والزكاة — ضريبةٌ سنويةٌ قدرها نحو 2.5٪ على الخمول من الثروة: واجبُ إعادة توزيعٍ لا قيدٌ على النشاط.", + "وما يبقى بعد ذلك عاديٌّ لا غرابة فيه: امتلاك أصولٍ حقيقية، وتحمّل مخاطرةٍ حقيقيةٍ مقابل عائدٍ حقيقي. لا رافعةَ ولا بيعَ على المكشوف ولا مشتقّات ولا بيعَ ما لا تملك. وهذا يجعل كيل في العمليّات أكثرَ تحفّظًا من حساب وساطةٍ عادي، لا أقل — فهو يستبعد معظم الأدوات التي انهارت في 2008.", + ], + }, { title: "وقائعُ محسوبة مقابل تصنيفاتٍ موثَّقة", body: [ @@ -114,6 +146,15 @@ export const compliance: LocalizedPage = { "والنموذج نفسه يشمل المنصّة ذاتها: فالسكة 14 ترفض أوامر الشراء الحيّة حتى يوثّق المشغّل اشتراكه في المنصّة، والسكة 17 تُشفّر القبض الحُكمي §65.4 — إذ إنّ الأصل الذي لا يمكن سحبُه قد لا يكون قد قُبِض قبضًا صحيحًا، فتُوثَّق قابليةُ السحب وتُنفَّذ، ولا تُفترض.", ], }, + { + title: "ثلاثة أنواع من القواعد — وأين يقع مصرفك منها", + body: [ + "هل يمكنك استخدام كيل إن كان مصرفك يدفع فائدةً أو يأخذها؟ نعم — فكيل لا يمسّ مصرفك أبدًا: يعمل على حساب تداولٍ واحد، ولا صلةَ له بمصرفك ولا رؤيةَ ولا سلطة. والسؤال الأدقّ هو: أين تعيش كلُّ قاعدة؟ وهناك ثلاثةُ أمكنةٍ بالضبط:", + "ما تزوّده أنت: التصنيفات — هل قطاع هذا الأصل مباح، وهل هو عينٌ مملوكة أم دَينٌ على مُصدر، وهل يُدفع عليه عائد. الحُكمُ مُدخَلٌ تسجّله أنت؛ وكيل ليس محرّك فتوى ولا يستنبطها أبدًا.", + "ما يُنفّذه المحرّك آليًّا: هذه خواصُّ ثابتةٌ لا آراء. لا ينفق الشراء إلا رصيدًا مُسوّى موجودًا سلفًا في حساب التداول — لا من مصرفٍ مرتبط أبدًا — والرصيدُ الذي لا يستطيع المحرّك قراءته يعني صفقةً تُرفض لا تُحاول. الصمتُ ليس إذنًا بالإنفاق.", + "ما لا يستطيع التحقّق منه، والعلاج: قد تدفع المنصّة فائدةً داخل الحساب نفسه — مكافآت USDC على الأرصدة الخاملة لدى Coinbase. وواجهة التداول لا تكشف مفتاحًا للمكافآت، فلا يمكن أتمتة ذلك؛ ورايةُ إعدادٍ تزعم «تعطيل المكافآت» ستكون كذبةً مريحة — ولهذا لا توجد. إنها خطوةٌ يدويةٌ واجبةٌ في دليل التشغيل. وحين تستحقّ فائدةٌ فتُحصى: يُبلّغ سجلُّ التنقية بالمبلغ الذي يجب التخلّص منه لا إمساكه. وتحريك المال فعلُ المشغّل، لا البرمجية أبدًا.", + ], + }, { title: "التنقية وقصرُ العمل على التداول الفوري", body: [ @@ -131,6 +172,14 @@ export const compliance: LocalizedPage = { "ولا يَعِد بربح — إذ لا تحقّق أيُّ عائلةٍ من القواعد المُصدَّرة ربحًا صافيًا عند رسوم الآخذ الفعلية، والمؤشّر المرجعي هو الشراء الدوري المنتظم (DCA).", ], }, + framing: { + title: "أهذا «القانون الشريعي»؟", + body: [ + "يُستخدم هذا التعبير أداةَ تخويفٍ في بعض الأماكن، والكلمةُ الضارّة فيه هي «القانون» — بما يوحي بفرض شيءٍ على الآخرين. وكيل نقيضُ ذلك: شخصٌ واحد يقيّد تداوله هو، ببرمجيةٍ ترفض أن تمنحه استثناءً مريحًا لاحقًا. لا يحكم سلوكَ أحدٍ غير سلوك المشغّل نفسه.", + "لا حُكمَ مضمّنًا في البرمجية أينما كان. فمشغّلان يتبعان مدرستين مختلفتين يحصلان على نتائج مختلفة من الشيفرة نفسها، لأن الحُكم مُدخَلٌ تسجّله لا مخرجٌ تتلقّاه. والوصفُ الصادق: محرّكُ امتثالٍ صُدِف أن بُني لإطارٍ أخلاقيٍّ واحد.", + "وللالسياق: كان تحريم الربا عقيدةً مسيحيةً نحو ألف عام — يجري عبر توما الأكويني والمجمع اللاتراني الثالث وقرونٍ من قانون الكنيسة، والقاعدةُ نفسها في سفر التثنية. فالإسلام أبقى على تحريمٍ تراخَت عنه أوروبا لأسبابٍ تجاريةٍ لا لاهوتية. ليس مستوردًا غريبًا؛ بل طريقًا لم يُسلَك.", + ], + }, attest: { title: "الأمرُ الأهمّ", body: [ @@ -167,14 +216,21 @@ export const compliance: LocalizedPage = { }, fr: { - rev: "2026-08-20.2", - translatedFromRev: "2026-08-20.2", + rev: "2026-08-22.1", + translatedFromRev: "2026-08-22.1", title: "La méthodologie de conformité, en termes simples", description: "Comment fonctionne la conformité Shariah de keel : un filtrage attesté qui bloque par défaut, le modèle d'attestation par (plateforme, produit), le contrôle qabd — et ce que keel n'est pas : ni une fatwa, ni un conseil financier, et sans examen par des savants.", intro: "La conformité de keel est conçue pour que chaque règle appliquée soit écrite, sourcée et fournie par vous, l'opérateur. Rien de religieux n'est déduit, imposé par défaut ni dissimulé dans le code. Cette page expose ce modèle en langage courant ; le document de base fiqh en donne l'énoncé de référence, règle par règle.", sections: [ + { + title: "Ce que le cadre exige de l'argent", + body: [ + "La gouvernance Shariah de la finance tient en quatre interdits et une obligation. Riba — l'intérêt : l'argent ne peut pas gagner de l'argent par lui-même ; un rendement doit venir du risque réel pris dans une activité réelle, pas d'un prêt à taux garanti. Gharar — l'incertitude excessive : les contrats doivent être clairs, et l'on ne vend pas ce que l'on ne possède pas ou n'a pas reçu. Maysir — le jeu : aucun pari à somme nulle présenté comme un investissement. Secteurs interdits — alcool, porc, jeux, industrie pour adultes, armes, finance conventionnelle à intérêt. Et la zakat, un prélèvement annuel d'environ 2,5 % sur la richesse oisive — une obligation de redistribution, non une restriction.", + "Ce qui subsiste n'a rien d'extraordinaire : posséder des actifs réels, prendre un risque véritable pour un rendement véritable. Pas de levier, pas de vente à découvert, pas de produits dérivés, pas de vente de ce que l'on ne détient pas. En pratique, cela rend keel plus prudent qu'un compte de courtage classique, pas moins — il exclut la plupart des instruments qui ont failli en 2008.", + ], + }, { title: "Ce qui se calcule, ce qui s'atteste", body: [ @@ -188,6 +244,15 @@ export const compliance: LocalizedPage = { "Le même modèle vaut pour la plateforme elle-même : le garde-fou 14 refuse tout achat réel tant que l'opérateur n'a pas attesté l'abonnement, et le garde-fou 17 encode le qabd §65.4 — un actif impossible à retirer n'a peut-être jamais été valablement possédé, si bien que la capacité de retrait est attestée puis appliquée, jamais présumée.", ], }, + { + title: "Trois sortes de règles — et la place de votre banque", + body: [ + "Peut-on utiliser keel si sa banque verse ou facture des intérêts ? Oui — keel ne touche jamais à votre banque. Il opère sur un seul compte de trading et n'a ni lien avec elle, ni visibilité sur elle, ni autorité sur elle. La vraie question est celle de la place de chaque règle, et il y en a exactement trois :", + "Ce que vous fournissez : les classifications — le secteur de cet actif est-il permissible, est-ce une chose possédée ('ayn) ou une créance sur un émetteur (dayn), verse-t-elle un rendement. Le jugement est une entrée que vous enregistrez ; keel n'est pas une machine à fatwas et n'en produit jamais.", + "Ce que le moteur applique mécaniquement : ce sont des invariants, pas des opinions. Un achat ne peut dépenser qu'un solde déjà réglé, déjà présent sur le compte de trading — jamais une banque liée — et un solde illisible pour le moteur est un ordre refusé, pas tenté. Le silence n'est pas un consentement à dépenser.", + "Ce qu'il ne peut pas vérifier, et le remède : une plateforme peut verser de l'intérêt au sein même du compte — les récompenses USDC de Coinbase sur les soldes oisifs. L'API de trading n'expose aucun interrupteur de récompenses, donc rien ne peut être automatisé ; un drapeau de configuration clamant « récompenses désactivées » serait un mensonge rassurant — il n'existe donc pas. C'est une étape manuelle obligatoire du runbook opérateur. Quand des intérêts courent, ils sont comptés : le registre de purification publie le montant à donner plutôt qu'à garder. Déplacer cet argent est l'acte de l'opérateur, jamais celui du logiciel.", + ], + }, { title: "La purification et la forme « comptant uniquement »", body: [ @@ -205,6 +270,14 @@ export const compliance: LocalizedPage = { "Il ne promet aucun gain : aucune famille de règles livrée ne dégage un résultat net positif aux frais de preneur réellement payés, et la référence est le simple achat périodique (DCA).", ], }, + framing: { + title: "Est-ce « la loi shariah » ?", + body: [ + "L'expression sert d'épouvantail en certains endroits, et le mot qui blesse est loi — il suggère quelque chose d'imposé aux autres. keel est l'inverse : une personne qui contraint son propre trading, avec un logiciel qui refuse de lui accorder plus tard une exception commode. La conduite de personne d'autre que l'opérateur n'est gouvernée.", + "Aucun jugement n'est encodé dans le logiciel. Deux opérateurs suivant des écoles différentes obtiennent des résultats différents avec le même code, parce que le jugement est une entrée que vous enregistrez, pas une sortie que vous recevez. La description honnête : un moteur de conformité qu'il est advenu qu'on bâtisse pour un cadre éthique précis.", + "Pour la perspective : l'interdiction de l'usure fut une doctrine chrétienne durant près de mille ans — elle traverse Thomas d'Aquin, le IIIe concile de Latran et des siècles de droit canon, et la même règle figure dans le Deutéronome. L'islam a conservé une interdiction que l'Europe a relâchée pour des raisons commerciales, non théologiques. Ce n'est pas une importation étrangère ; c'est une route non prise.", + ], + }, attest: { title: "La commande qui compte", body: [ From c6d5e37ad403a9920bf2f7c36db2f7aa456c0158 Mon Sep 17 00:00:00 2001 From: Elmehdi Aitbrahim Date: Fri, 21 Aug 2026 21:07:49 -0400 Subject: [PATCH 2/2] chore: drop stray lighthouse script --- lh.mjs | 21 --------------------- 1 file changed, 21 deletions(-) delete mode 100644 lh.mjs diff --git a/lh.mjs b/lh.mjs deleted file mode 100644 index 82a905d..0000000 --- a/lh.mjs +++ /dev/null @@ -1,21 +0,0 @@ -import puppeteer from "puppeteer-core"; -import { spawnSync } from "node:child_process"; - -const browser = await puppeteer.launch({ - executablePath: "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome", - defaultViewport: null, - args: ["--remote-debugging-port=9222"], -}); -const pages = ["/en/", "/en/compare/", "/en/news/", "/ar/"]; -for (const p of pages) { - const r = spawnSync("npx", ["lighthouse", `https://keeltrading.com${p}`, "--port=9222", "--quiet", "--chrome-flags=--headless=new", "--only-categories=performance,accessibility,best-practices,seo", "--output=json", "--output-path=stdout"], { encoding: "utf8", maxBuffer: 64 * 1024 * 1024 }); - try { - const report = JSON.parse(r.stdout); - const cats = Object.fromEntries(Object.entries(report.categories).map(([k, v]) => [k, Math.round(v.score * 100)])); - const metrics = report.audits; - console.log(p, JSON.stringify({ ...cats, LCP: metrics["largest-contentful-paint"]?.displayValue, CLS: metrics["cumulative-layout-shift"]?.displayValue })); - } catch { - console.log(p, "PARSE-FAIL", (r.stdout || r.stderr || "").slice(0, 200)); - } -} -await browser.close();