Download index.html from getreadystack/cert-lifetime-lint: direct link, hf CLI and curl.
- Browser
- Download file 30.4 kB
-
https://huggingface.co/spaces/getreadystack/cert-lifetime-lint/resolve/main/index.html
- Command line
-
hf download hf://spaces/getreadystack/cert-lifetime-lint/index.html
-
curl -L -o index.html https://huggingface.co/spaces/getreadystack/cert-lifetime-lint/resolve/main/index.html
30.4 kB
| <html lang="en"><meta charset="utf-8"> | |
| <meta name="viewport" content="width=device-width,initial-scale=1"> | |
| <title>Cert Lifetime Lint — TLS validity caps 200/100/47</title> | |
| <meta name="description" content="Finds the renewal settings in your repo that outlive the public TLS cap: 200 days since 15 March 2026, 100 days from 15 March 2027, 47 days from 15 March 2029."> | |
| <style> | |
| :root{color-scheme:light dark} | |
| body{margin:0;font:16px/1.6 system-ui,-apple-system,"Segoe UI",sans-serif; | |
| background:#fbfbfa;color:#16161d} | |
| @media(prefers-color-scheme:dark){body{background:#14141a;color:#e8e8ea}} | |
| .wrap{max-width:880px;margin:0 auto;padding:28px 20px 64px} | |
| h1{font-size:1.7rem;line-height:1.25;margin:.2em 0 .3em} | |
| p.sub{margin:0 0 1.4em;opacity:.8} | |
| textarea{width:100%;min-height:230px;font:13px/1.5 ui-monospace,Menlo,Consolas,monospace; | |
| padding:12px;border:1px solid #c9c9d1;border-radius:10px;background:transparent;color:inherit; | |
| box-sizing:border-box} | |
| button{font:600 15px/1 inherit;padding:12px 22px;border:0;border-radius:10px; | |
| background:#1d4ed8;color:#fff;cursor:pointer;margin:12px 0} | |
| button:hover{background:#1a43bd} | |
| table{border-collapse:collapse;width:100%;margin-top:14px;font-size:14px} | |
| th,td{border-bottom:1px solid #d8d8e0;padding:8px 6px;text-align:left;vertical-align:top} | |
| th{font-weight:600;opacity:.75} | |
| td.n{width:64px;font:13px ui-monospace,monospace;opacity:.7} | |
| .ok{padding:14px;border-radius:10px;background:#e8f5ec;color:#14532d;margin-top:14px} | |
| @media(prefers-color-scheme:dark){.ok{background:#15321f;color:#bbf7d0}} | |
| .next{margin-top:30px;padding:18px;border:1px solid #d8d8e0;border-radius:12px} | |
| .next h2{font-size:1.05rem;margin:0 0 .5em} | |
| .next ul{margin:.3em 0 1em 1.1em;padding:0} | |
| </style> | |
| <div class="wrap"> | |
| <h1>Cert Lifetime Lint — TLS validity caps 200/100/47</h1> | |
| <p class="sub">Finds the renewal settings in your repo that outlive the public TLS cap: 200 days since 15 March 2026, 100 days from 15 March 2027, 47 days from 15 March 2029.</p> | |
| <textarea id="src" placeholder="Paste a Kubernetes manifest, Terraform file, crontab or shell script here"># k8s/prod-cert.yaml | |
| spec: | |
| duration: 8760h | |
| # scripts/bootstrap.sh | |
| openssl req -x509 -newkey rsa:2048 -days 825 -out edge.pem | |
| # infra/tls.tf | |
| validity_period_hours = 8760 | |
| # crontab | |
| 0 0 1 */6 * /usr/bin/certbot renew --quiet | |
| # monitoring/expiry.py | |
| MAX_VALIDITY_DAYS = 398</textarea> | |
| <button id="go">Check this config</button> | |
| <div id="out"></div> | |
| <div class="next"><h2>In the full version</h2><ul><li>Scan every file in the repo</li><li>Dated evidence report</li><li>CI output</li><li><a href="https://getreadystack.com/api/buy/cl/polar_cl_WKA2GllrJspsrScNLUGtqn4DgnIEsTN1FpAtT42RfQY">Get the full version — $29</a></li></ul></div> | |
| </div> | |
| <script> | |
| var RULES=[{"pattern": "-days\\s+(?:2[0-9]{2}|[3-9][0-9]{2}|[0-9]{4,})", "flags": "", "sev": "error", "message": "openssl -days is 200 or more. A public CA has not issued that since 15 March 2026 (200-day cap); private and internal CAs are exempt.", "fix": "-days 90"}, {"pattern": "-validity\\s+(?:2[0-9]{2}|[3-9][0-9]{2}|[0-9]{4,})", "flags": "", "sev": "error", "message": "keytool -validity is 200 or more, over the 200-day public TLS cap in force since 15 March 2026. The cap falls to 100 days on 15 March 2027.", "fix": "-validity 90"}, {"pattern": "duration:\\s*[\"']?(?:4[89][0-9]{2}|[5-9][0-9]{3}|[0-9]{5,})h", "flags": "", "sev": "error", "message": "cert-manager duration is 4800h (200 days) or more. A public issuer refuses it; the ACME order fails and the Certificate never becomes Ready.", "fix": "duration: 2160h"}, {"pattern": "duration:\\s*[\"']?(?:2[0-9]{2}|[3-9][0-9]{2}|[0-9]{4,})d\\b", "flags": "", "sev": "error", "message": "Certificate duration of 200 days or more is over the public TLS cap. 100 days becomes the cap on 15 March 2027 and 47 days on 15 March 2029.", "fix": "duration: 90d"}, {"pattern": "renewBefore:\\s*[\"']?(?:1[1-9][0-9]{2}|[2-9][0-9]{3}|[0-9]{5,})h", "flags": "", "sev": "warn", "message": "renewBefore of 1100h (about 46 days) or more is longer than a 47-day certificate lives from 15 March 2029. cert-manager rejects renewBefore greater than or equal to duration.", "fix": "renewBefore: 360h"}, {"pattern": "validity_period_hours\\s*=\\s*(?:4[89][0-9]{2}|[5-9][0-9]{3}|[0-9]{5,})", "flags": "", "sev": "error", "message": "validity_period_hours is 4800 (200 days) or more, over the public TLS cap in force since 15 March 2026.", "fix": "validity_period_hours = 2160"}, {"pattern": "min_days_remaining\\s*=\\s*[\"']?(?:[6-9][0-9]|[0-9]{3,})", "flags": "", "sev": "warn", "message": "min_days_remaining of 60 or more never clears once certificates last 47 days (15 March 2029). Terraform then re-issues on every apply.", "fix": "min_days_remaining = 21"}, {"pattern": "(?:expiry|not-after|notAfter|maxTLSCertDuration|defaultTLSCertDuration)[\"']?\\s*[:=]\\s*[\"']?(?:4[89][0-9]{2}|[5-9][0-9]{3}|[0-9]{5,})h", "flags": "i", "sev": "error", "message": "This CA profile issues certificates of 4800h (200 days) or longer, over the public TLS cap since 15 March 2026.", "fix": "\"expiry\": \"2160h\""}, {"pattern": "not_after:\\s*[\"']?\\+(?:2[0-9]{2}|[3-9][0-9]{2}|[0-9]{4,})d", "flags": "", "sev": "error", "message": "not_after of +200d or longer is over the public TLS cap. The cap falls to 100 days on 15 March 2027.", "fix": "not_after: \"+90d\""}, {"pattern": "\\*/(?:[3-9]|1[0-2])\\s+\\*\\s.*\\b(?:certbot|acme|lego|renew)\\b", "flags": "i", "sev": "error", "message": "This renewal job runs every 3 months or less often. A 100-day certificate (15 March 2027) can expire between two runs.", "fix": "0 3 * * * certbot renew"}, {"pattern": "@(?:yearly|annually|monthly)\\b.*\\b(?:cert|certbot|acme|lego|renew|ssl|tls)\\b", "flags": "i", "sev": "warn", "message": "A monthly or yearly renewal cadence leaves no retry room once certificates last 47 days (15 March 2029).", "fix": "@daily certbot renew"}, {"pattern": "\\b398\\b(?=[^\\n]*(?:day|valid|cert|tls|ssl|expir))|(?:day|valid|cert|tls|ssl|expir)[^\\n]*\\b398\\b", "flags": "i", "sev": "warn", "message": "398 days stopped being the maximum on 14 March 2026. The cap is 200 days now, 100 days from 15 March 2027 and 47 days from 15 March 2029.", "fix": "200"}, {"pattern": "(?:warn|alert|expir[a-z]*|renew[a-z]*)[_a-z]*_?days\\s*[:=]\\s*[\"']?(?:[6-9][0-9]|[0-9]{3,})", "flags": "i", "sev": "warn", "message": "An expiry warning threshold of 60 days or more fires permanently once certificates last 47 days (15 March 2029).", "fix": "14"}, {"pattern": "-checkend\\s+(?:[5-9][0-9]{6}|[0-9]{8,})", "flags": "", "sev": "warn", "message": "-checkend of 5000000 seconds (about 58 days) is longer than a 47-day certificate lives, so the check is always true from 15 March 2029.", "fix": "-checkend 1209600"}, {"pattern": "certificatesDuration\\s*[:=]\\s*[\"']?(?:4[89][0-9]{2}|[5-9][0-9]{3}|[0-9]{5,})", "flags": "", "sev": "error", "message": "Traefik certificatesDuration is 4800 hours (200 days) or more, over the public TLS cap since 15 March 2026.", "fix": "certificatesDuration = 2160"}, {"pattern": "(?:cert[a-z]*|tls|ssl)[^\\n]{0,40}\\b(?:1|one)[\\s-]*year\\b", "flags": "i", "sev": "warn", "message": "A one-year public TLS certificate can no longer be issued. The maximum has been 200 days since 15 March 2026.", "fix": "200 days"}]; | |
| function scan(t){var L=String(t).split(/\r?\n/),h=[],i,r,re; | |
| for(i=0;i<L.length;i++){for(var k=0;k<RULES.length;k++){r=RULES[k]; | |
| try{re=new RegExp(r.pattern,r.flags||'')}catch(e){continue} | |
| if(re.test(L[i]))h.push({line:i+1,msg:r.message})}} | |
| return h} | |
| document.getElementById('go').onclick=function(){ | |
| var t=document.getElementById('src').value,o=document.getElementById('out'); | |
| if(!t.trim()){o.innerHTML='';return} | |
| var h=scan(t); | |
| if(!h.length){o.innerHTML='<div class="ok">Nothing in this file exceeds the public TLS caps.</div>';return} | |
| var s='<table><tr><th>Line</th><th>Finding</th></tr>'; | |
| for(var i=0;i<h.length;i++){s+='<tr><td class="n">'+h[i].line+'</td><td>'+ | |
| String(h[i].msg).replace(/&/g,'&').replace(/</g,'<')+'</td></tr>'} | |
| o.innerHTML=s+'</table>'}; | |
| document.getElementById('go').click(); | |
| </script> | |
| </html> | |
| <section data-yj-install="free" style="margin:2rem auto 1rem;max-width:46rem;padding:1.25rem 1.25rem;border:1px solid #cfe3d6;border-radius:12px;background:#f6fbf8;font:16px/1.6 system-ui,-apple-system,Segoe UI,sans-serif"><h2 style="margin:0 0 .4rem;font-size:1.2rem">Install free</h2><p style="margin:0 0 .8rem;color:#41505f">Free for the file open in your editor - no key, no limit. The workspace sweep and the report ask for a key.</p><a href="https://marketplace.visualstudio.com/items?itemName=ReadyStack.cert-lifetime-lint" rel="noopener" data-yj-install-door="mp" onclick="try{navigator.sendBeacon('/api/ev',new Blob([JSON.stringify({t:'install',slug:'cert-lifetime-lint',src:'mp',path:location.pathname})],{type:'application/json'}))}catch(e){}" style="display:inline-block;margin:0 .5rem .5rem 0;padding:.6rem 1rem;border-radius:8px;background:#0f7b3f;color:#fff;text-decoration:none;font-weight:600">VS Code Marketplace</a><a href="https://www.npmjs.com/package/@readystack/cert-lifetime-lint" rel="noopener" data-yj-install-door="npm" onclick="try{navigator.sendBeacon('/api/ev',new Blob([JSON.stringify({t:'install',slug:'cert-lifetime-lint',src:'npm',path:location.pathname})],{type:'application/json'}))}catch(e){}" style="display:inline-block;margin:0 .5rem .5rem 0;padding:.6rem 1rem;border-radius:8px;background:#0f7b3f;color:#fff;text-decoration:none;font-weight:600">npm</a><a href="https://hub.docker.com/r/getreadystack/cert-lifetime-lint" rel="noopener" data-yj-install-door="docker" onclick="try{navigator.sendBeacon('/api/ev',new Blob([JSON.stringify({t:'install',slug:'cert-lifetime-lint',src:'docker',path:location.pathname})],{type:'application/json'}))}catch(e){}" style="display:inline-block;margin:0 .5rem .5rem 0;padding:.6rem 1rem;border-radius:8px;background:#0f7b3f;color:#fff;text-decoration:none;font-weight:600">Docker Hub</a><pre style="margin:.5rem 0 0;padding:.6rem .8rem;background:#f3f5f8;border-radius:8px;font-size:.9rem;overflow:auto">ext install ReadyStack.cert-lifetime-lint | |
| npx @readystack/cert-lifetime-lint <file> | |
| docker run --rm -v "$PWD:/w" getreadystack/cert-lifetime-lint /w</pre><p style="margin:.6rem 0 0;font-size:.92rem;color:#41505f"><a href="https://getreadystack.com/api/buy/team" rel="nofollow" style="color:#0f7b3f;text-decoration:underline">Team? One key for every ReadyStack linter, 5 seats, $149 once</a></p></section> | |
| <section data-yj-alert="rule" style="margin:1rem auto 1.5rem;max-width:46rem;padding:1.1rem 1.25rem;border:1px solid #d9dee6;border-radius:12px;background:#fbfcfd;font:15px/1.55 system-ui,-apple-system,Segoe UI,sans-serif"><b style="font-size:1.05rem">Get one email when this rule changes</b><div style="opacity:.78;margin:.35rem 0 .8rem">We watch the regulation and vendor sources behind Cert Lifetime Lint - TLS validity caps 200/100/47 every day. When a rule changes, you get a single email with what changed and the updated check. No newsletter.</div><div style="display:flex;gap:8px;flex-wrap:wrap"><input id="yjae" type="email" placeholder="you@example.com" style="flex:1;min-width:210px;padding:11px 13px;border:1px solid rgba(140,150,165,.34);border-radius:8px;background:#fff;color:inherit"><button id="yjab" style="padding:11px 18px;border-radius:8px;border:0;cursor:pointer;background:#0f7b3f;color:#fff;font:600 15px system-ui,sans-serif">Notify me</button></div><div id="yjao" hidden style="margin-top:.6rem;color:#0f7b3f">You're on the list. One email when it changes.</div><script>(function(){var b=document.getElementById("yjab"),e=document.getElementById("yjae");if(!b||!e)return;b.addEventListener("click",function(){var v=e.value.trim();if(!/.+@.+\..+/.test(v))return;try{localStorage.setItem("yj_em","1");}catch(_){}fetch(location.origin+"/api/subscribe",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({email:v,slug:"cert-lifetime-lint",lang:"en",source:"rule_alert"+((b.closest&&b.closest("section")&&b.closest("section").getAttribute("data-yj-moved"))?"_at_result":"")})}).then(function(r){var o=document.getElementById("yjao");o.hidden=false;if(!r.ok){o.textContent="Could not save - please try again.";}}).catch(function(){var o=document.getElementById("yjao");o.hidden=false;o.textContent="Could not save - please try again.";});});})();</script><script>(function(){var e=document.getElementById("yjae"),s=e&&e.closest?e.closest("section"):null,o=document.getElementById("out");if(!s||!o)return;var d=false;function mv(ev){if(d)return;if(ev&&ev.target&&s.contains(ev.target))return;d=true;var b=o;while(b.parentNode&&b.parentNode!==document.body&&b.parentNode.tagName!=="MAIN"&&b.parentNode.tagName!=="ARTICLE")b=b.parentNode;if(!b.parentNode||b===s||b.contains(s))return;try{b.parentNode.insertBefore(s,b.nextSibling);s.setAttribute("data-yj-moved","1");}catch(_){}}document.addEventListener("input",mv,true);document.addEventListener("change",mv,true);})();</script></section> | |
| <section data-yj-cta="paid" style="margin:3rem auto;max-width:46rem;padding:1.5rem 1.25rem;border:1px solid #d8dee9;border-radius:12px;background:#fbfcfe;font:16px/1.6 system-ui,-apple-system,Segoe UI,sans-serif"><h2 style="margin:0 0 .5rem;font-size:1.25rem">Get the complete version <span class="yj-price">$29</span></h2><p style="margin:.25rem 0 1rem;color:#41505f">This page is the working piece. The full pack has everything below.</p><p style="margin:0 0 1rem;color:#41505f">Finds the renewal settings in your repo that outlive the public TLS cap: 200 days since 15 March 2026, 100 days from 15 March 2027, 47 days from 15 March 2029.</p><p style="margin:0 0 1rem;color:#5b6b7c;font-size:.94rem">Hosted certificate-expiry monitoring is $25-29 a month.</p><a href="https://getreadystack.com/api/buy/cl/polar_cl_WKA2GllrJspsrScNLUGtqn4DgnIEsTN1FpAtT42RfQY" rel="nofollow" style="display:inline-block;padding:.7rem 1.3rem;border-radius:8px;background:#1b64f2;color:#fff;text-decoration:none;font-weight:600">Buy the full version — $29</a></section> | |
| <section data-yj="yj-facts-v1" style="max-width:920px;margin:48px auto 0;padding:0 20px;font-family:system-ui,sans-serif"><style>.yj-article p{font:400 15.5px/1.7 system-ui,sans-serif;color:#26313b;margin:0 0 12px;max-width:72ch}.yj-article h2,.yj-article h3,.yj-article h4{font:600 17px/1.35 system-ui,sans-serif;color:#1b1f24;margin:22px 0 8px}.yj-article table{border-collapse:collapse;margin:10px 0 16px;font:14px/1.45 system-ui,sans-serif}.yj-article th,.yj-article td{border:1px solid #e2e6ea;padding:6px 10px;text-align:left}.yj-article th{background:#f4f6f8}.yj-article pre{background:#0f1720;color:#e6edf3;padding:12px 14px;border-radius:8px;overflow:auto;font:13px/1.5 ui-monospace,Menlo,monospace}.yj-article code{font:13px ui-monospace,Menlo,monospace;background:#f1f3f5;padding:1px 4px;border-radius:3px}.yj-article pre code{background:none;padding:0}.yj-article ul,.yj-article ol{font:15px/1.6 system-ui,sans-serif;color:#26313b}</style><h2 style="font:600 21px/1.3 system-ui,sans-serif;margin:34px 0 6px;color:#1b1f24">Questions people ask</h2><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">What does Cert Lifetime Lint actually do?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">It reads the certificate settings written in your repository - cert-manager duration and renewBefore, Terraform validity_period_hours, cfssl and step-ca expiry, openssl -days, keytool -validity, Ansible not_after, Traefik certificatesDuration and renewal cron schedules - and marks every value that a public certificate authority will no longer issue, with the replacement line beside it.</p></details><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">Who is it for?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">Platform, SRE and DevOps engineers who own the renewal path: the person whose pager fires when an ACME order is refused or a certificate expires at 3am. If you maintain cert-manager Certificates, Terraform tls resources, a cfssl or step-ca profile, or the crontab that runs certbot, this reads your files.</p></details><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">Why will a free SSL checker not do this?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">Free checkers connect to a live host and report the certificate that is already serving traffic. That certificate is valid, so they stay silent. The failure lives in the config you have not applied yet - a duration of 8760h that the CA will refuse at the next issuance. Nothing that probes a live port can see it.</p></details><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">What is free and what needs a licence?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">Free covers one whole file: open a manifest, run the check, and all 16 rules report every over-cap line with its fix and severity. Nothing is watermarked, timed or locked after N runs. The licence changes the scope only - every file in the repository at once, a dated exported report, and JSON output for CI.</p></details><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">What would this cost otherwise?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">Hosted certificate-expiry monitoring runs about $25 to $29 a month, and it still only watches certificates that are already live. Auditing renewal config by hand means grepping every repository each time the cap steps down, which it does on 15 March 2027 and again on 15 March 2029. This is $29 once.</p></details><script type="application/ld+json" data-yj="yj-faq-ld">{"@context": "https://schema.org", "@type": "FAQPage", "mainEntity": [{"@type": "Question", "name": "What does Cert Lifetime Lint actually do?", "acceptedAnswer": {"@type": "Answer", "text": "It reads the certificate settings written in your repository - cert-manager duration and renewBefore, Terraform validity_period_hours, cfssl and step-ca expiry, openssl -days, keytool -validity, Ansible not_after, Traefik certificatesDuration and renewal cron schedules - and marks every value that a public certificate authority will no longer issue, with the replacement line beside it."}}, {"@type": "Question", "name": "Who is it for?", "acceptedAnswer": {"@type": "Answer", "text": "Platform, SRE and DevOps engineers who own the renewal path: the person whose pager fires when an ACME order is refused or a certificate expires at 3am. If you maintain cert-manager Certificates, Terraform tls resources, a cfssl or step-ca profile, or the crontab that runs certbot, this reads your files."}}, {"@type": "Question", "name": "Why will a free SSL checker not do this?", "acceptedAnswer": {"@type": "Answer", "text": "Free checkers connect to a live host and report the certificate that is already serving traffic. That certificate is valid, so they stay silent. The failure lives in the config you have not applied yet - a duration of 8760h that the CA will refuse at the next issuance. Nothing that probes a live port can see it."}}, {"@type": "Question", "name": "What is free and what needs a licence?", "acceptedAnswer": {"@type": "Answer", "text": "Free covers one whole file: open a manifest, run the check, and all 16 rules report every over-cap line with its fix and severity. Nothing is watermarked, timed or locked after N runs. The licence changes the scope only - every file in the repository at once, a dated exported report, and JSON output for CI."}}, {"@type": "Question", "name": "What would this cost otherwise?", "acceptedAnswer": {"@type": "Answer", "text": "Hosted certificate-expiry monitoring runs about $25 to $29 a month, and it still only watches certificates that are already live. Auditing renewal config by hand means grepping every repository each time the cap steps down, which it does on 15 March 2027 and again on 15 March 2029. This is $29 once."}}]}</script><div id="yj-ask" style="margin:36px 0 0;padding:18px 18px 16px;border:1px solid #e2e6ea;border-radius:10px;background:#fbfbfc"><h2 style="font:600 19px/1.3 system-ui,sans-serif;margin:0 0 4px;color:#1b1f24">Ask about this tool</h2><p style="font:400 13.5px/1.5 system-ui,sans-serif;margin:0 0 12px;color:#5b6570">One question, answered by the person who built it. Your email only if you want the answer sent.</p><form onsubmit="return yjAsk(event)" style="display:grid;gap:8px"><textarea name="question" required minlength="10" maxlength="2000" rows="3" placeholder="Your question" style="font:14px/1.5 system-ui,sans-serif;padding:10px;border:1px solid #cfd6dd;border-radius:6px"></textarea><div style="display:flex;gap:8px;flex-wrap:wrap"><input type="email" name="email" placeholder="Email (optional)" style="flex:1;min-width:200px;font:14px system-ui,sans-serif;padding:9px 10px;border:1px solid #cfd6dd;border-radius:6px"><button type="submit" style="font:600 14px system-ui,sans-serif;padding:9px 18px;border:0;border-radius:6px;background:#1b1f24;color:#fff;cursor:pointer">Send</button></div><p id="yj-ask-msg" style="font:13px system-ui,sans-serif;margin:2px 0 0;color:#0a6238;min-height:1em"></p></form><script>function yjAsk(e){e.preventDefault();var f=e.target,m=document.getElementById("yj-ask-msg");m.style.color="#0a6238";fetch(location.origin+"/api/ask",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({sku:0,slug:"cert-lifetime-lint",lang:"en",question:f.question.value,email:f.email.value,page:location.pathname})}).then(function(r){if(!r.ok)throw 0;m.textContent="Thanks \u2014 noted. Answers land on this page and, if you left an email, in your inbox.";f.question.value="";}).catch(function(){m.style.color="#a8551f";m.textContent="Could not send. Please try again in a minute.";});return false;}</script></div></section><section class="yj-related" style="max-width:920px;margin:56px auto 40px;padding:0 20px"><h2 style="font:600 21px/1.3 system-ui,sans-serif;margin:0 0 6px;color:#1b1f24">Same job, next tool</h2><p style="font:400 14.5px/1.5 system-ui,sans-serif;margin:0 0 20px;color:#5b6570">Free in the browser. The full version is a one-time licence.</p><div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(250px,1fr));gap:14px"><a class="yj-rel-c" href="https://getreadystack.com/tools/cert-lifetime-lint-47day"><span class="yj-rel-t">Cert Lifetime Lint - 200/100/47-Day TLS Audit</span><span class="yj-rel-p">$29</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/config-dsl-lint-snippet-pack"><span class="yj-rel-t">Config & DSL Lint + Snippets — 9 Sets</span><span class="yj-rel-p">$29</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/devicetree-config-language-pack"><span class="yj-rel-t">DeviceTree Config Audit: 23 dtc-Style Rules, 50 Snippets</span><span class="yj-rel-p">$29</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/gtags-workspace-config-kit"><span class="yj-rel-t">GNU GLOBAL (gtags) Workspace Setup Kit</span><span class="yj-rel-p">$29</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/gnu-global-cpp-vscode-config-pack"><span class="yj-rel-t">GNU Global C/C++ .vscode Config Pack</span><span class="yj-rel-p">$29</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/ats-resume-template"><span class="yj-rel-t">ATS Resume Template — 6 Fill-In Word Documents for US Job Applications (Manage</span><span class="yj-rel-p">$29</span></a></div><style>.yj-rel-c{display:flex;flex-direction:column;gap:8px;padding:15px 16px;border:1px solid #e2e6ea;border-radius:6px;text-decoration:none;background:#fff}.yj-rel-c:hover{border-color:#b9c2cb;background:#fafbfc}.yj-rel-t{font:500 14.5px/1.42 system-ui,sans-serif;color:#1b1f24}.yj-rel-p{font:600 13px system-ui,sans-serif;color:#0a6238}</style></section><!--yj-share-s101--><div style="margin:28px 0 8px;text-align:center"><button id="yjcp" type="button" style="font:inherit;padding:10px 18px;border:1px solid #8884;border-radius:10px;background:transparent;cursor:pointer">Copy link</button><button id="yjimg" type="button" style="font:inherit;padding:10px 18px;margin-left:8px;border:1px solid #8884;border-radius:10px;background:transparent;cursor:pointer">Save result as image</button></div><script>(function(){var b=document.getElementById("yjimg");if(!b)return;b.addEventListener("click",function(){var o=document.getElementById("out");var txt=(o&&(o.innerText||o.textContent)||"").trim();if(!txt){b.textContent="(no result yet)";setTimeout(function(){b.textContent="Save result as image";},1500);return;}var lines=txt.split(String.fromCharCode(10)).filter(function(x){return x.trim();}).slice(0,10);var W=1200,P=56,c=document.createElement("canvas");c.width=W;var lh=38,H=P*2+70+lines.length*lh+60;c.height=H;var x=c.getContext("2d");x.fillStyle="#1a1512";x.fillRect(0,0,W,H);x.fillStyle="#f3e9d2";x.font="600 34px system-ui,Segoe UI,Arial";var t=(document.title||"").split("|")[0].trim();x.fillText(t.slice(0,54),P,P+34);x.font="22px ui-monospace,Menlo,Consolas,monospace";x.fillStyle="#e8dcc4";for(var i=0;i<lines.length;i++){x.fillText(lines[i].slice(0,88),P,P+70+i*lh+22);}x.font="600 22px system-ui,Segoe UI,Arial";x.fillStyle="#c9a227";var d=location.hostname.replace("www.","");x.fillText(d,W-P-x.measureText(d).width,H-P+6);c.toBlob(function(bl){var u=URL.createObjectURL(bl),a=document.createElement("a");a.href=u;a.download=(t||"result").replace(/[^a-z0-9]+/gi,"-").toLowerCase()+"-"+d+".png";document.body.appendChild(a);a.click();setTimeout(function(){URL.revokeObjectURL(u);a.remove();},2000);},"image/png");});})();</script><script>(function(){var b=document.getElementById("yjcp");if(!b)return;var t=b.textContent;b.addEventListener("click",function(){var u=location.href.split("#")[0];function d(){b.textContent="Link copied";setTimeout(function(){b.textContent=t;},1800);}if(navigator.clipboard&&navigator.clipboard.writeText){navigator.clipboard.writeText(u).then(d,function(){prompt("",u);});}else{prompt("",u);}});})();</script><div class="yj-home-find"><div class="yj-lgbar"><a href="/" class="yj-lg on">EN</a><a href="/de/" class="yj-lg">DE</a><a href="/ja/" class="yj-lg">JA</a><a href="/es/" class="yj-lg">ES</a><a href="/pt/" class="yj-lg">PT</a></div><section class="yj-find"><h2>Find a tool</h2><input id="yjq" type="search" autocomplete="off" placeholder="Type a product name, format or task"><div id="yjr"></div></section><style>.yj-lgbar{position:absolute;top:14px;right:18px;display:flex;gap:6px;z-index:60}.yj-lg{font:600 12px/1 system-ui,sans-serif;padding:6px 9px;border-radius:4px;text-decoration:none;color:#8b96a3;border:1px solid rgba(140,150,165,.28)}.yj-lg:hover{color:#e8eaed;border-color:rgba(160,175,195,.6)}.yj-lg.on{color:#0e1116;background:#cfd6de;border-color:#cfd6de}.yj-find{max-width:760px;margin:34px auto 8px;padding:0 20px}.yj-find h2{font:600 20px/1.3 system-ui,sans-serif;margin:0 0 10px}#yjq{width:100%;box-sizing:border-box;padding:13px 15px;font:400 15.5px system-ui,sans-serif;border:1px solid rgba(140,150,165,.34);border-radius:8px;background:rgba(255,255,255,.04);color:inherit}#yjq:focus{outline:none;border-color:#7FD1C4}#yjr{margin-top:10px;display:flex;flex-direction:column;gap:6px}#yjr a{display:flex;justify-content:space-between;gap:12px;padding:11px 14px;border:1px solid rgba(140,150,165,.22);border-radius:6px;text-decoration:none;font:400 14.5px/1.4 system-ui,sans-serif;color:inherit}#yjr a:hover{border-color:#7FD1C4}#yjr .pr{font:600 13px system-ui,sans-serif;color:#5fbf9a;white-space:nowrap}</style><script>(function(){var q=document.getElementById("yjq"),r=document.getElementById("yjr"),D=null;function load(){if(D)return Promise.resolve(D);return fetch("/search-index.json").then(function(x){return x.json();}).then(function(j){D=j;return j;}).catch(function(){D=[];return D;});}function esc(t){return String(t).replace(/[&<>"]/g,function(c){return {"&":"&","<":"<",">":">",'"':"""}[c];});}function run(){var v=q.value.trim().toLowerCase();if(v.length<2){r.innerHTML="";return;}load().then(function(j){var w=v.split(/\s+/),o=[];for(var i=0;i<j.length&&o.length<12;i++){var t=j[i].t.toLowerCase();var ok=true;for(var k=0;k<w.length;k++){if(t.indexOf(w[k])<0){ok=false;break;}}if(ok)o.push(j[i]);}r.innerHTML=o.length?o.map(function(x){return "<a href=\""+esc(x.u)+"\">"+"<span>"+esc(x.t)+"</span>"+(x.p?"<span class=\"pr\">$"+x.p+"</span>":"")+"</a>";}).join(""):"";});}q.addEventListener("input",run);q.addEventListener("focus",load);})();</script></div> | |
| <script data-yj="yj-view-beacon">(function(){try{var p=location.pathname;var k='yjv:'+p;if(sessionStorage.getItem(k))return;sessionStorage.setItem(k,'1');fetch('/api/ev',{method:'POST',keepalive:true,headers:{'Content-Type':'application/json'},body:JSON.stringify({path:p,lang:"en",ref:(document.referrer||''),src:(function(){try{return (new URLSearchParams(location.search).get('ref')||'').replace(/[^a-z_]/g,'').slice(0,12)}catch(e){return ''}})()})}).catch(function(){});}catch(e){}})();</script> | |
| <script data-yj="yj-use-beacon">(function(){try{var o=document.getElementById('out');if(!o)return;var p=location.pathname,k='yju:'+p,sent=false;function fire(){if(sent||sessionStorage.getItem(k))return;if(!(o.textContent||'').replace(/\s+/g,'').length)return;sent=true;sessionStorage.setItem(k,'1');fetch('/api/ev',{method:'POST',keepalive:true,headers:{'Content-Type':'application/json'},body:JSON.stringify({path:p,lang:"en",t:'use',src:(function(){try{return (new URLSearchParams(location.search).get('ref')||'').replace(/[^a-z_]/g,'').slice(0,12)}catch(e){return ''}})()})}).catch(function(){});}var mo=new MutationObserver(function(){fire();if(sent)mo.disconnect();});mo.observe(o,{childList:true,characterData:true,subtree:true});}catch(e){}})();</script> | |