File size: 45,184 Bytes
a052e04 a3b3c70 a052e04 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 | <!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Sentry Migration 2026 — check your code for free</title>
<meta name="description" content="This free browser tool finds Sentry settings that GlitchTip ingest drops, DSNs still on sentry.io, and FSL self-hosted Sentry images.">
<style>
:root{color-scheme:light}body{margin:0;font:15px/1.55 system-ui,-apple-system,"Segoe UI",sans-serif;background:#faf9f5;color:#1a1512}
.wrap{max-width:960px;margin:0 auto;padding:28px 18px 60px}h1{font-size:26px;margin:0 0 6px}.lede{color:#4a403a;margin:0 0 16px}
textarea{width:100%;min-height:260px;font:13px/1.45 ui-monospace,Menlo,Consolas,monospace;padding:12px;border:1px solid #d9d0c4;border-radius:8px;box-sizing:border-box}
.bar{display:flex;gap:10px;align-items:center;margin:12px 0}button{font:600 15px system-ui;padding:10px 18px;border:0;border-radius:8px;background:#1a1512;color:#fff;cursor:pointer}
input[type=date]{font:14px system-ui;padding:8px;border:1px solid #d9d0c4;border-radius:6px}
table{border-collapse:collapse;width:100%;font-size:14px;margin-top:10px}th,td{text-align:left;padding:8px 10px;border-bottom:1px solid #eee;vertical-align:top}th{font-size:12px;letter-spacing:.06em;text-transform:uppercase;color:#7a6e65}
.sev.error{color:#a8551f;font-weight:700}.sev.warn,.sev.warning{color:#96691f}.ok{color:#2a6446;font-weight:600}.sum{font-weight:600}
.note{font-size:13px;color:#7a6e65;margin-top:18px}#paid{margin-top:22px;padding:14px 16px;border:1px solid #e2d8cb;border-radius:10px;background:#fffdfa}
</style>
<script data-grow-initializer="">!(function(){window.growMe||((window.growMe=function(e){window.growMe._.push(e);}),(window.growMe._=[]));var e=document.createElement("script");(e.type="text/javascript"),(e.src="https://faves.grow.me/main.js"),(e.defer=!0),e.setAttribute("data-grow-faves-site-id","U2l0ZTphZjljMzgyMC0yMmU5LTRhYjgtYTIxNS03OTRhMGY1N2ExYzM=");var t=document.getElementsByTagName("script")[0];t.parentNode.insertBefore(e,t);})();</script>
<!--yj-canon-s101--><link rel="canonical" href="https://getreadystack.com/tools/glitchtip-migration-lint"><style data-yj-mfit="1">#out{max-width:100%}#out td,#out th{overflow-wrap:anywhere}#out pre,#out code{white-space:pre-wrap;overflow-wrap:anywhere}@media (max-width:640px){table{display:block;max-width:100%;overflow-x:auto}pre{max-width:100%;overflow-x:auto}code,a{overflow-wrap:anywhere}img,svg,video,canvas,iframe{max-width:100%}textarea,input,select{max-width:100%;box-sizing:border-box}.yj-lgbar{position:static!important;justify-content:flex-end;margin:0 0 10px}#out table:not(:has(tr>:nth-child(4))){display:table;table-layout:fixed;width:100%}#out table:has(tr>:nth-child(4)),#out table:has(tr>:nth-child(4)) tbody,#out table:has(tr>:nth-child(4)) tr,#out table:has(tr>:nth-child(4)) td{display:block;width:auto;overflow:visible}#out table:has(tr>:nth-child(4)) thead{display:none}#out table:has(tr>:nth-child(4)) tr{padding:10px 0;border-bottom:1px solid rgba(128,128,128,.28)}#out table:has(tr>:nth-child(4)) td{border:0;padding:1px 0}#out table:has(tr>:nth-child(4)) td:nth-child(-n+3){display:inline-block;margin-right:10px;font-size:12px;opacity:.78}}</style>
</head>
<body>
<div class="wrap">
<h1>Sentry Migration 2026 — check your code for free</h1>
<p class="lede">Finds Sentry SDK settings GlitchTip ingest drops (Session Replay, profiling, cron check-ins, sessions, attachments, metrics), DSNs still on sentry.io, and FSL self-hosted Sentry images. Runs entirely in your browser — nothing is uploaded.</p>
<textarea id="src" spellcheck="false">// sentry.client.config.ts - sample Next.js app moving from Sentry SaaS to GlitchTip
import * as Sentry from "@sentry/nextjs";
Sentry.init({
dsn: "https://3f2a9c1e7b@o451234.ingest.us.sentry.io/4507712345",
tracesSampleRate: 1.0,
autoSessionTracking: true,
replaysSessionSampleRate: 0.1,
replaysOnErrorSampleRate: 1.0,
profilesSampleRate: 1.0,
integrations: [
Sentry.replayIntegration(),
Sentry.browserProfilingIntegration(),
Sentry.feedbackIntegration({ colorScheme: "system" }),
],
});
export async function nightlyInvoiceJob(run: () => Promise<void>) {
await Sentry.withMonitor("nightly-invoices", run, {
schedule: { type: "crontab", value: "0 2 * * *" },
});
}
export function reportUpload(file: Uint8Array) {
Sentry.getCurrentScope().addAttachment({ filename: "upload.bin", data: file });
Sentry.metrics.count("upload.failed", 1);
}</textarea>
<div class="bar"><button id="go">Check</button><label>as of <input type="date" id="today"></label></div>
<div id="out" aria-live="polite"></div>
<div id="paid"><strong>Free here:</strong> Paste or open one Sentry init file: every setting GlitchTip drops or still routes to sentry.io is listed with its line, the envelope item it produces, and the GlitchTip-side fix. <strong>In the editor:</strong> Workspace migration report: every Sentry setting across all apps, CI files and compose files in the repo, exported as a Markdown or CSV checklist for the cutover pull request. — that part asks for a licence key. $29 once · one licence key per person or team seat. Sentry Team is $26/mo billed annually. <a href="https://getreadystack.com/api/buy/cl/polar_cl_298CgJtnZYoccavOy9Fai6GwNUqJK4Mdl6DuM1ZTicE" data-polar-checkout data-polar-checkout-theme="light">Get the workspace migration report — $29</a></div>
<script defer data-auto-init src="https://cdn.jsdelivr.net/npm/@polar-sh/checkout@latest/dist/embed.global.js"></script>
<p class="note">Same engine as the VS Code extension, byte for byte.</p>
</div>
<script>var GT_RULES = [
{
"id": "GT_DSN_SENTRY_IO",
"sev": "error",
"title": "DSN still points at sentry.io",
"item": "",
"pattern": "ingest\\.(?:[a-z]{2}\\.)?sentry\\.io|@sentry\\.io/\\d",
"msg": "This DSN sends every event to Sentry SaaS, not GlitchTip. After cutover the Sentry bill keeps running and GlitchTip shows nothing.",
"fix": "Replace it with the DSN from your GlitchTip project settings page (https://\u003ckey\u003e@\u003cyour-glitchtip-host\u003e/\u003cproject-id\u003e)."
},
{
"id": "GT_CLI_URL_SENTRY",
"sev": "error",
"title": "Upload URL still points at sentry.io",
"item": "",
"pattern": "^\\s*(?:url\\s*=|SENTRY_URL\\s*[=:]|defaults\\.url\\s*=)\\s*['\\\"]?https://(?:[a-z0-9-]+\\.)?sentry\\.io",
"msg": "Source maps and debug files from this config still go to sentry.io, so GlitchTip stack traces stay minified.",
"fix": "Set url to your GlitchTip host. glitchtip-cli reads .sentryclirc and SENTRY_URL, so the same file keeps working: glitchtip-cli sourcemaps upload ./dist --org \u003corg\u003e --project \u003cproject\u003e."
},
{
"id": "GT_REPLAY_INTEGRATION",
"sev": "error",
"title": "Session Replay integration",
"item": "replay_event / replay_recording",
"pattern": "replayIntegration\\s*\\(|replayCanvasIntegration\\s*\\(|new\\s+(?:Sentry\\.)?Replay\\s*\\(|['\\\"]@sentry/replay['\\\"]",
"msg": "Session Replay sends replay_event and replay_recording items. GlitchTip ingest lists both as ignored item types: the replays are dropped and the SDK still records them in the browser.",
"fix": "Remove the replay integration before switching the DSN. GlitchTip has no replay viewer."
},
{
"id": "GT_REPLAY_RATES",
"sev": "error",
"title": "Replay sample rate",
"item": "replay_event / replay_recording",
"pattern": "replaysSessionSampleRate|replaysOnErrorSampleRate|sessionReplay\\.(?:sessionSampleRate|onErrorSampleRate)|sessionReplay\\s*:\\s*\\{",
"msg": "Replay sample rates only feed Session Replay. GlitchTip ingest ignores replay_event and replay_recording, so this rate buys CPU and upload bytes for nothing.",
"fix": "Delete the replay sample rates (or set them to 0) together with the replay integration."
},
{
"id": "GT_PROFILING_RATE",
"sev": "error",
"title": "Profiling sample rate",
"item": "profile / profile_chunk",
"pattern": "profilesSampleRate|profiles_sample_rate|profileSessionSampleRate|profile_session_sample_rate|profilesSampler|profiles_sampler|profileLifecycle|profile_lifecycle",
"msg": "Profiling sends profile or profile_chunk items. GlitchTip ingest ignores both, so no profile ever appears.",
"fix": "Remove the profiling rate. For slow endpoints use GlitchTip Performance, which reads transactions and their spans."
},
{
"id": "GT_PROFILING_INTEGRATION",
"sev": "error",
"title": "Profiling integration or package",
"item": "profile / profile_chunk",
"pattern": "browserProfilingIntegration\\s*\\(|nodeProfilingIntegration\\s*\\(|@sentry/profiling-node|ProfilingIntegration\\s*\\(",
"msg": "The profiling integration samples the call stack and uploads profile items that GlitchTip ingest ignores.",
"fix": "Remove the integration and the @sentry/profiling-node dependency; keep tracesSampleRate for GlitchTip Performance."
},
{
"id": "GT_CRON_CHECKIN",
"sev": "error",
"title": "Cron monitor check-in",
"item": "check_in",
"pattern": "captureCheckIn\\s*\\(|withMonitor\\s*\\(|capture_checkin\\s*\\(|@monitor\\s*\\(|sentry_sdk\\.crons|monitor_config\\s*=|CaptureCheckIn\\s*\\(|instrumentCron\\s*\\(|instrumentNodeCron\\s*\\(|instrumentNodeSchedule\\s*\\(",
"msg": "Cron monitors send check_in items. GlitchTip ingest ignores check_in, so a scheduled job that stops running raises no alert.",
"fix": "Create a GlitchTip uptime monitor of type Heartbeat and ping its URL when the job succeeds, or wrap the job: glitchtip-cli monitors run \u003cENDPOINT_UUID\u003e -- \u003ccommand\u003e."
},
{
"id": "GT_SESSIONS",
"sev": "warn",
"title": "Release-health sessions on",
"item": "session / sessions",
"pattern": "autoSessionTracking\\s*:\\s*true|auto_session_tracking\\s*=\\s*True|enableAutoSessionTracking\\s*[:=]\\s*(?:true|YES)|browserSessionIntegration\\s*\\(",
"msg": "Session tracking sends session items. GlitchTip's own SDK pages set this option to false with the note 'GlitchTip does not support sessions'.",
"fix": "Set autoSessionTracking: false (JS) or auto_session_tracking=False (Python), as in GlitchTip's SDK docs."
},
{
"id": "GT_SESSION_API",
"sev": "warn",
"title": "Manual session call",
"item": "session",
"pattern": "\\.startSession\\s*\\(|\\.captureSession\\s*\\(|\\.endSession\\s*\\(|start_session\\s*\\(|end_session\\s*\\(",
"msg": "Manual session calls produce session items that GlitchTip ingest ignores; crash-free rates will not exist after the move.",
"fix": "Remove the call, or count sessions in your own analytics."
},
{
"id": "GT_ATTACHMENT",
"sev": "warn",
"title": "Event attachment",
"item": "attachment",
"pattern": "addAttachment\\s*\\(|add_attachment\\s*\\(|AddAttachment\\s*\\(|attachScreenshot\\s*[:=]\\s*(?:true|YES)|attachViewHierarchy\\s*[:=]\\s*(?:true|YES)|attach_screenshot\\s*=\\s*True",
"msg": "Attachments travel as attachment items. GlitchTip ingest ignores them (it only parses a native minidump when no event came with it), so the file never reaches the issue.",
"fix": "Put the small facts you need into setContext / set_context or tags; keep large files in your own storage and add their URL as context."
},
{
"id": "GT_METRICS",
"sev": "warn",
"title": "Sentry metrics API",
"item": "trace_metric",
"pattern": "Sentry\\.metrics\\.\\w+\\s*\\(|sentry_sdk\\.metrics\\.|SentrySdk\\.Metrics\\.",
"msg": "The metrics API sends trace_metric items. GlitchTip ingest ignores trace_metric, so the counter stays at zero.",
"fix": "Send the value as a log line (enableLogs: true and Sentry.logger), which GlitchTip ingests, or move the metric to your metrics stack."
},
{
"id": "GT_STANDALONE_SPANS",
"sev": "warn",
"title": "Standalone span streaming",
"item": "span",
"pattern": "enableStandalone\\w*Spans|traceLifecycle\\s*:\\s*['\\\"]stream|trace_lifecycle['\\\"]?\\s*[:=]\\s*['\\\"]stream",
"msg": "Standalone or streamed spans are sent as span items. GlitchTip ingest ignores span items; it reads spans only inside a transaction.",
"fix": "Turn the experiment off so spans ship inside their transaction."
},
{
"id": "GT_TRACES_FULL",
"sev": "warn",
"title": "Every transaction sampled",
"item": "transaction",
"pattern": "tracesSampleRate\\s*:\\s*1(?:\\.0+)?\\b|traces_sample_rate\\s*=\\s*1(?:\\.0+)?\\b|enableTracing\\s*:\\s*true|enable_tracing\\s*=\\s*True",
"msg": "A 1.0 rate sends every transaction, and hosted GlitchTip counts each one as an event: Free is 1,000 events/mo, Small $15/mo is 100k. Past the quota GlitchTip throttles, and at 2x the quota it blocks fully, errors included.",
"fix": "Use a low rate as GlitchTip's FAQ advises: tracesSampleRate: 0.01 (1 percent)."
},
{
"id": "GT_SENTRY_CLI_DEFAULT",
"sev": "info",
"title": "sentry-cli upload step",
"item": "",
"pattern": "getsentry/action-release|sentry-cli\\s+(?:sourcemaps|releases|debug-files)|npx\\s+@sentry/cli",
"msg": "sentry-cli uploads to https://sentry.io/ unless a url is set, so this CI step may still feed the old project.",
"fix": "Set SENTRY_URL to your GlitchTip host for this step, or call glitchtip-cli sourcemaps inject / upload instead."
},
{
"id": "GT_SELFHOSTED_SENTRY",
"sev": "info",
"title": "Self-hosted Sentry image (FSL)",
"item": "",
"pattern": "image:\\s*['\\\"]?(?:ghcr\\.io/)?getsentry/(?:sentry|snuba|relay|symbolicator|vroom)\\b",
"msg": "Self-hosted Sentry's web app is licensed FSL-1.1-Apache-2.0 (open.sentry.io/licensing): free to run for your business, but not as a commercial Sentry-like offering until each release turns Apache-2.0 after two years. GlitchTip is MIT.",
"fix": "If you resell error tracking or host it for clients, run glitchtip/glitchtip (PostgreSQL 14+, one service, Valkey optional) instead."
},
{
"id": "GT_CLIENT_REPORTS",
"sev": "info",
"title": "Client reports on",
"item": "client_report",
"pattern": "sendClientReports\\s*:\\s*true|send_client_reports\\s*=\\s*True",
"msg": "Client reports (counts of events the SDK discarded) are sent as client_report items, which GlitchTip ingest ignores.",
"fix": "Set sendClientReports: false to save the request."
}
];</script>
<script>/* Sentry -> GlitchTip migration lint. Same file runs in VS Code (node) and in the free web page. */
(function () {
var RULES = (typeof module !== 'undefined') ? require('./rules.json') : window.GT_RULES;
// Item types GlitchTip ingest accepts vs. ignores: glitchtip-backend apps/event_ingest/schema.py (SupportedItemType / IgnoredItemType)
var INGESTED = ['event', 'transaction', 'user_report', 'feedback', 'log', 'otel_log'];
var COMPILED = RULES.map(function (r) { return { r: r, re: new RegExp(r.pattern) }; });
function isComment(line) { return /^\s*(\/\/|#(?!\s*!)|\*|\/\*|<!--|;)/.test(line); }
function check(text, opts) {
opts = opts || {};
var lines = String(text || '').split(/\r?\n/);
var findings = [];
var dropped = {};
lines.forEach(function (line, i) {
if (isComment(line)) return;
COMPILED.forEach(function (c) {
if (!c.re.test(line)) return;
if (c.r.item && c.r.item !== 'transaction') dropped[c.r.item] = 1;
var what = c.r.item ? ' [envelope item: ' + c.r.item + ']' : '';
findings.push({ check: c.r.id, sev: c.r.sev, msg: c.r.msg + what + ' Fix: ' + c.r.fix, line: i + 1, title: c.r.title, fix: c.r.fix, item: c.r.item });
});
});
var errors = findings.filter(function (f) { return f.sev === 'error'; }).length;
return { findings: findings, summary: { total: findings.length, errors: errors, dropped_item_types: Object.keys(dropped), ingested: INGESTED } };
}
var api = { engine: { check: check }, RULES: RULES, RULE_COUNT: RULES.length };
if (typeof module !== 'undefined') module.exports = api;
if (typeof window !== 'undefined') window.GTENGINE = api;
})();
</script>
<script>// ⛔뼈대 — kit/scaffold.py 가 찍는다 (s143). findings [{check,sev,msg,line?}] → 글/HTML. 같은 파일이 Node 와 브라우저에서 산다.
'use strict';
function escapeHtml(s) { return String(s == null ? '' : s).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"'); }
function summaryLine(res) {
const f = (res && res.findings) || []; const e = f.filter(function (x) { return String(x.sev || 'error').toLowerCase().startsWith('err'); }).length;
return f.length ? (f.length + ' finding' + (f.length === 1 ? '' : 's') + ' (' + e + ' error' + (e === 1 ? '' : 's') + ')') : 'Clean — no findings.';
}
function toText(res, label) {
const f = (res && res.findings) || [];
const lines = ['== ' + (label || '') + ' — ' + summaryLine(res)];
f.forEach(function (x) { lines.push(' ' + String(x.sev || 'error').toUpperCase().slice(0, 4) + ' ' + (x.line ? 'L' + x.line + ' ' : '') + (x.check || x.id || '') + ' ' + (x.msg || x.message || '')); });
return lines.join('\n');
}
function toHtml(res) {
const f = (res && res.findings) || [];
if (!f.length) return '<p class="ok">' + escapeHtml(summaryLine(res)) + '</p>';
const rows = f.map(function (x) { return '<tr><td class="sev ' + escapeHtml(String(x.sev || 'error')) + '">' + escapeHtml(String(x.sev || 'error')) + '</td><td>' + (x.line ? 'L' + escapeHtml(x.line) : '') + '</td><td><code>' + escapeHtml(x.check || x.id || '') + '</code></td><td>' + escapeHtml(x.msg || x.message || '') + '</td></tr>'; }).join('');
return '<p class="sum">' + escapeHtml(summaryLine(res)) + '</p><table><thead><tr><th>Level</th><th>Line</th><th>Check</th><th>What</th></tr></thead><tbody>' + rows + '</tbody></table>';
}
var GTREPORT = { escapeHtml: escapeHtml, summaryLine: summaryLine, toText: toText, toHtml: toHtml };
if (typeof module !== 'undefined' && module.exports) module.exports = GTREPORT;
else window.GTREPORT = GTREPORT;
</script>
<script>
(function(){
var src=document.getElementById('src'), out=document.getElementById('out'), go=document.getElementById('go'), today=document.getElementById('today');
today.value=new Date().toISOString().slice(0,10);
function run(){ try { var res=window.GTENGINE.engine.check(src.value,{today:today.value}); out.innerHTML=window.GTREPORT.toHtml(res); } catch(e){ out.innerHTML='<p class="sev error">Could not run: '+String(e.message||e)+'</p>'; } }
go.onclick=run; if (src.value.trim()) run();
})();
</script>
<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://open-vsx.org/extension/readystack/glitchtip-migration-lint" rel="noopener" data-yj-install-door="ovsx" onclick="try{navigator.sendBeacon('/api/ev',new Blob([JSON.stringify({t:'install',slug:'glitchtip-migration-lint',src:'ovsx',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">Open VSX (Cursor, VSCodium)</a><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 GlitchTip Migration Lint for Sentry 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:"glitchtip-migration-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);document.addEventListener("click",function(ev){var b=ev.target&&ev.target.closest?ev.target.closest("button,input[type=submit]"):null;if(b&&!b.closest("[data-yj],[data-yj-cta],[data-yj-install],[data-yj-alert],.yj-find,.yj-related,#yj-ask,.yjp"))mv(ev);},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 full version <span class="yj-price">$29</span></h2><p class="yj-cta-paid" style="margin:.25rem 0 .6rem;color:#1f2a37">Workspace migration report ($29 once, one licence key per person or team seat): every Sentry setting across all apps, CI files and compose files in the repo, exported as a Markdown or CSV checklist for the cutover pull request.</p><p class="yj-cta-pay" style="margin:.25rem 0 1rem;color:#5b6b7c;font-size:.94rem">One payment, one licence key for this tool. The key is shown right after payment.</p><p class="yj-cta-anchor" style="margin:0 0 1rem;color:#5b6b7c;font-size:.94rem">Sentry Team is $26/mo billed annually; GlitchTip hosted Small is $15/mo for 100k events</p><a href="https://getreadystack.com/api/buy/cl/polar_cl_298CgJtnZYoccavOy9Fai6GwNUqJK4Mdl6DuM1ZTicE" 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>
<!--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>
<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><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><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><p style="font:400 13px/1.5 system-ui,sans-serif;color:#6b7680;margin:0 0 14px"><time datetime="2026-09-29">Updated 2026-09-29</time> · ReadyStack</p><script type="application/ld+json">{"@context":"https://schema.org","@type":"WebPage","dateModified":"2026-09-29","author":{"@type":"Organization","name":"ReadyStack"}}</script><h2 style="font:600 21px/1.3 system-ui,sans-serif;margin:0 0 6px;color:#1b1f24">Worked example</h2><p style="font:400 14.5px/1.5 system-ui,sans-serif;margin:0 0 16px;color:#5b6570">Real numbers from this tool, line by line.</p><div class="yj-article"><p><img src="https://getreadystack.com/img/promo/sku395439_result_card.jpg" alt="GlitchTip Migration Lint for Sentry" loading="lazy" style="max-width:100%;border-radius:6px"></p>
<p>Six Sentry features stopped arriving when our sample Next.js app pointed its DSN at GlitchTip: one lint pass over sentry.client.config.ts returned 11 findings, 7 errors. If you are an engineering team or agency moving from Sentry to GlitchTip, this is the part the migration guides skip.</p>
<p>Why teams move. Sentry's web app is licensed FSL-1.1-Apache-2.0 (open.sentry.io/licensing): you may run it for your own business, but not as a commercial Sentry-like offering until each release converts to Apache-2.0 after two years. Sentry Team costs $26/mo billed annually and Business $80/mo. GlitchTip is MIT-licensed, speaks the Sentry SDK protocol, and hosted plans start free at 1,000 events/mo, with Small at $15/mo for 100k events.</p>
<p>What breaks without a sound. Because GlitchTip accepts the same SDKs, the swap looks like a one-line DSN change. It is not. GlitchTip's ingest code (apps/event_ingest/schema.py in glitchtip-backend) accepts six envelope item types: event, transaction, user_report, feedback, log and otel_log. It lists session, attachment, check_in, profile, replay_event, replay_recording, span and trace_metric as ignored. The SDK keeps sending them, and the data is simply not stored.</p>
<p>In our sample file that meant:</p>
<ul><li>replayIntegration() plus two replay sample rates: Session Replay still records in every browser, then gets dropped.</li><li>profilesSampleRate: 1.0 and browserProfilingIntegration(): no profile ever appears.</li><li>Sentry.withMonitor("nightly-invoices"): the cron check-in is dropped, so a nightly job that stops running raises no alert. This is the expensive one.</li><li>autoSessionTracking: true: GlitchTip's own SDK pages set it to false with the note "GlitchTip does not support sessions".</li><li>addAttachment(upload.bin): the file never reaches the issue.</li><li>Sentry.metrics.count("upload.failed"): the counter stays at zero.</li></ul>
<p>Two more findings are not about dropped data. The DSN still pointed at o451234.ingest.us.sentry.io, so events would keep flowing to Sentry. And tracesSampleRate: 1.0 sends every transaction; hosted GlitchTip counts each one as an event, throttles past the quota and blocks fully at 2x the quota, errors included. GlitchTip's FAQ suggests 0.01.</p>
<p>The fixes. Remove the replay and profiling integrations and rates. Replace cron monitors with a GlitchTip uptime monitor of type Heartbeat, or wrap the job with glitchtip-cli monitors run. Set autoSessionTracking to false. Move attachment facts into setContext. Send metric values as logs with enableLogs: true, which GlitchTip ingests. The fixed file returns 0 findings.</p>
<p>How the lint works. GlitchTip Migration Lint is a VS Code extension with 16 rules. Open a file and every finding appears in the Problems panel with its line, the envelope item it produces and the GlitchTip-side fix. The same engine runs on a free web page where you paste a file. It reads JS/TS, Python, Dart, Swift, Kotlin, Java, C#, YAML, .env, .properties and .sentryclirc, and it also flags sentry-cli steps with no url (they upload to https://sentry.io/ by default) and self-hosted getsentry images.</p>
<p>Why not a chatbot. A chatbot will happily rewrite your Sentry.init, but it does not know GlitchTip's ignore list, and it only sees the snippet you paste, not the CI step or compose file next to it.</p>
<p>Free and paid. Linting the open file is free, with no key, and finishes the job for a single-app repo. The workspace migration report covers every app, CI file and compose file and exports a Markdown or CSV checklist for the cutover pull request.</p></div><h2 style="font:600 19px/1.3 system-ui,sans-serif;margin:30px 0 10px;color:#1b1f24">15 seconds — what it actually does</h2><video controls muted playsinline preload="none" poster="/img/promo/sku395439_result_card.jpg" style="width:100%;max-width:360px;border-radius:10px;display:block;margin:0 auto"><source src="/media/sku395439_short.mp4" type="video/mp4"></video><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 GlitchTip Migration Lint do?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">It scans Sentry SDK setup code, .sentryclirc, CI and docker-compose files for settings GlitchTip will not ingest: Session Replay, profiling, cron check-ins, sessions, attachments and metrics. It also flags DSNs and upload URLs still pointing at sentry.io and self-hosted Sentry images under the FSL licence. It has 16 rules, and each finding names the line, the envelope item and the GlitchTip-side fix.</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 this Sentry to GlitchTip lint for?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">Engineering teams, agencies and self-hosters moving from Sentry SaaS or self-hosted Sentry to GlitchTip, in the US, the EU or anywhere else. It suits the person who owns the cutover pull request: a Next.js, Node, Django or mobile app whose Sentry.init was written for Sentry features and is about to point its DSN at a GlitchTip host.</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 not just change the DSN or ask a chatbot?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">Changing the DSN works for errors, and GlitchTip returns success for everything else too. Its ingest code ignores session, attachment, check_in, profile, replay and trace_metric items, so Session Replay, profiles, cron check-ins and metrics vanish without an error. A chatbot does not know that list and only sees the snippet you paste.</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 key?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">Linting one open or pasted file is free, with no key and no limit: every finding shows its line, the dropped envelope item and the fix, from all 16 rules. The paid workspace report ($29 once, one licence key per person or team seat) scans every app, CI file and compose file in the repo and exports a Markdown or CSV checklist.</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">How does the cost compare with staying on Sentry?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">Sentry Team is $26/mo billed annually and Business is $80/mo. Hosted GlitchTip is free up to 1,000 events/mo, Small is $15/mo for 100k events and Medium $50/mo for 500k. The lint itself is free per file; the $29 workspace report is a one-time payment, not a subscription, for the whole repo checklist.</p></details><script type="application/ld+json" data-yj="yj-faq-ld">{"@context": "https://schema.org", "@type": "FAQPage", "mainEntity": [{"@type": "Question", "name": "What does GlitchTip Migration Lint do?", "acceptedAnswer": {"@type": "Answer", "text": "It scans Sentry SDK setup code, .sentryclirc, CI and docker-compose files for settings GlitchTip will not ingest: Session Replay, profiling, cron check-ins, sessions, attachments and metrics. It also flags DSNs and upload URLs still pointing at sentry.io and self-hosted Sentry images under the FSL licence. It has 16 rules, and each finding names the line, the envelope item and the GlitchTip-side fix."}}, {"@type": "Question", "name": "Who is this Sentry to GlitchTip lint for?", "acceptedAnswer": {"@type": "Answer", "text": "Engineering teams, agencies and self-hosters moving from Sentry SaaS or self-hosted Sentry to GlitchTip, in the US, the EU or anywhere else. It suits the person who owns the cutover pull request: a Next.js, Node, Django or mobile app whose Sentry.init was written for Sentry features and is about to point its DSN at a GlitchTip host."}}, {"@type": "Question", "name": "Why not just change the DSN or ask a chatbot?", "acceptedAnswer": {"@type": "Answer", "text": "Changing the DSN works for errors, and GlitchTip returns success for everything else too. Its ingest code ignores session, attachment, check_in, profile, replay and trace_metric items, so Session Replay, profiles, cron check-ins and metrics vanish without an error. A chatbot does not know that list and only sees the snippet you paste."}}, {"@type": "Question", "name": "What is free and what needs a licence key?", "acceptedAnswer": {"@type": "Answer", "text": "Linting one open or pasted file is free, with no key and no limit: every finding shows its line, the dropped envelope item and the fix, from all 16 rules. The paid workspace report ($29 once, one licence key per person or team seat) scans every app, CI file and compose file in the repo and exports a Markdown or CSV checklist."}}, {"@type": "Question", "name": "How does the cost compare with staying on Sentry?", "acceptedAnswer": {"@type": "Answer", "text": "Sentry Team is $26/mo billed annually and Business is $80/mo. Hosted GlitchTip is free up to 1,000 events/mo, Small is $15/mo for 100k events and Medium $50/mo for 500k. The lint itself is free per file; the $29 workspace report is a one-time payment, not a subscription, for the whole repo checklist."}}]}</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:395439,slug:"glitchtip-migration-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/personal-data-map-dsar-audit"><span class="yj-rel-t">Personal Data Map: find every column a GDPR subject access request has to reac</span><span class="yj-rel-p">$29</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/portfolio-website-template"><span class="yj-rel-t">Portfolio Website Template — Freelance Designer Portfolio & Case Study Site (8</span><span class="yj-rel-p">$49</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/real-estate-webflow-template"><span class="yj-rel-t">Real Estate Webflow Template Alternative — 7-Page Self-Hosted HTML Broker Site</span><span class="yj-rel-p">$49</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/webflow-restaurant-template"><span class="yj-rel-t">Webflow Restaurant Template — delivered as self-contained HTML you own: 26 pag</span><span class="yj-rel-p">$49</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/webflow-saas-template"><span class="yj-rel-t">Webflow SaaS Template — a 16-Page SaaS Marketing Site with the Copy Already Wr</span><span class="yj-rel-p">$49</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/almalinux-migration-lint"><span class="yj-rel-t">AlmaLinux Migration Lint — CentOS 7/8</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></body>
</html>
|